From ba93a9b5d6f6fc8fe018eaf5da5922b5e7e10d03 Mon Sep 17 00:00:00 2001 From: Kyle Bayes Date: Mon, 3 Jun 2024 10:07:20 -0700 Subject: [PATCH 01/32] Remove mj_makeEmptyFileVFS in favor of mj_addBufferVFS from MuJoCo codebases. PiperOrigin-RevId: 639824744 Change-Id: I7746c71b0be88a61e76c4622d605356701881563 --- test/user/user_objects_test.cc | 4 +--- test/xml/xml_native_reader_test.cc | 14 ++++---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/test/user/user_objects_test.cc b/test/user/user_objects_test.cc index f23a7574..bb4e7e3e 100644 --- a/test/user/user_objects_test.cc +++ b/test/user/user_objects_test.cc @@ -363,9 +363,7 @@ TEST_F(ContentTypeTest, TextureLoadPng) { // load VFS on the heap auto vfs = std::make_unique(); mj_defaultVFS(vfs.get()); - mj_makeEmptyFileVFS(vfs.get(), filename, 105); - int i = mj_findFileVFS(vfs.get(), filename); - memcpy(vfs->filedata[i], tiny, tiny_sz); + mj_addBufferVFS(vfs.get(), filename, tiny, tiny_sz); // loading the file should be successful mjModel* model = LoadModelFromString(xml, error, error_sz, vfs.get()); diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index 41a1ca73..1f225fa4 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -554,14 +554,9 @@ TEST_F(XMLReaderTest, IncludeTest) { auto vfs = std::make_unique(); mj_defaultVFS(vfs.get()); - mj_makeEmptyFileVFS(vfs.get(), "model1.xml", sizeof(xml1)); - std::memcpy(vfs->filedata[vfs->nfile - 1], xml1, sizeof(xml1)); - - mj_makeEmptyFileVFS(vfs.get(), "model2.xml", sizeof(xml2)); - std::memcpy(vfs->filedata[vfs->nfile - 1], xml2, sizeof(xml2)); - - mj_makeEmptyFileVFS(vfs.get(), "model3.xml", sizeof(xml3)); - std::memcpy(vfs->filedata[vfs->nfile - 1], xml3, sizeof(xml3)); + mj_addBufferVFS(vfs.get(), "model1.xml", xml1, sizeof(xml1)); + mj_addBufferVFS(vfs.get(), "model2.xml", xml2, sizeof(xml2)); + mj_addBufferVFS(vfs.get(), "model3.xml", xml3, sizeof(xml3)); std::array error; mjModel* model = LoadModelFromString(xml, error.data(), @@ -606,8 +601,7 @@ TEST_F(XMLReaderTest, IncludeSameFileTest) { auto vfs = std::make_unique(); mj_defaultVFS(vfs.get()); - mj_makeEmptyFileVFS(vfs.get(), "model1.xml", sizeof(xml1)); - std::memcpy(vfs->filedata[vfs->nfile - 1], xml1, sizeof(xml1)); + mj_addBufferVFS(vfs.get(), "model1.xml", xml1, sizeof(xml1)); std::array error; mjModel* model = LoadModelFromString(xml, error.data(), error.size(), From ff167e91d9b7daec64a17a625b62fc19d9bfacd8 Mon Sep 17 00:00:00 2001 From: Meghha Dhoke Date: Mon, 3 Jun 2024 11:57:22 -0700 Subject: [PATCH 02/32] Updated the version number and release date to be current release v#3.1.6 PiperOrigin-RevId: 639864767 Change-Id: I94a2c81fe125c99bdcfca07b557eebeadca542e9 --- doc/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index b56a0ec8..cfe17f86 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -2,7 +2,7 @@ Changelog ========= -Upcoming version (not yet released) +Version 3.1.6 (Jun 3, 2024) ----------------------------------- General From 21bc6e5ce41b09d80a8b8df30f6fc866b81a152b Mon Sep 17 00:00:00 2001 From: Meghha Dhoke Date: Mon, 3 Jun 2024 12:23:58 -0700 Subject: [PATCH 03/32] Trimmed underline to match the current format. PiperOrigin-RevId: 639873230 Change-Id: Id065af04b6364e720d14873ab55fabff75e94efb --- doc/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index cfe17f86..d1e6e023 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -3,7 +3,7 @@ Changelog ========= Version 3.1.6 (Jun 3, 2024) ------------------------------------ +--------------------------- General ^^^^^^^ From 1bf90af0e0e553b3a1d1145df6962c4fb709a899 Mon Sep 17 00:00:00 2001 From: Meghha Dhoke Date: Tue, 4 Jun 2024 01:05:46 +0100 Subject: [PATCH 04/32] Bumping mjVERSIONSTRING from 3.1.6 to next version 3.1.7 PiperOrigin-RevId: 639959152 Change-Id: I7b88bae6ea56b0173f00c6f0899ba4a156614f01 --- CMakeLists.txt | 2 +- dist/mujoco.rc | 8 ++++---- dist/simulate.rc | 8 ++++---- doc/APIreference/APIglobals.rst | 2 +- doc/unity.rst | 4 ++-- include/mujoco/mujoco.h | 2 +- mjx/pyproject.toml | 8 ++++---- python/mujoco/CMakeLists.txt | 4 ++-- python/mujoco/mjpython/Info.plist | 8 ++++---- python/pyproject.toml | 6 +++--- sample/CMakeLists.txt | 2 +- simulate/CMakeLists.txt | 2 +- src/engine/engine_support.c | 4 ++-- unity/Editor/Bindings/MujocoBinaryRetriever.cs | 4 ++-- unity/Runtime/Bindings/MjBindings.cs | 2 +- unity/package.json | 2 +- 16 files changed, 34 insertions(+), 34 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c1ac72a7..c13da243 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON) project( mujoco - VERSION 3.1.6 + VERSION 3.1.7 DESCRIPTION "MuJoCo Physics Simulator" HOMEPAGE_URL "https://mujoco.org" ) diff --git a/dist/mujoco.rc b/dist/mujoco.rc index 6ae69239..3ddbe5fb 100644 --- a/dist/mujoco.rc +++ b/dist/mujoco.rc @@ -1,6 +1,6 @@ 1 VERSIONINFO -FILEVERSION 3,1,6,0 -PRODUCTVERSION 3,1,6,0 +FILEVERSION 3,1,7,0 +PRODUCTVERSION 3,1,7,0 FILEOS 0x4 FILETYPE 0x1 { @@ -9,9 +9,9 @@ FILETYPE 0x1 BLOCK "040904b0" { VALUE "ProductName", "MuJoCo" - VALUE "ProductVersion", "3.1.6" + VALUE "ProductVersion", "3.1.7" VALUE "FileDescription", "MuJoCo" - VALUE "FileVersion", "3.1.6" + VALUE "FileVersion", "3.1.7" VALUE "InternalName", "mujoco.dll" VALUE "OriginalFilename", "mujoco.dll" VALUE "CompanyName", "Google DeepMind" diff --git a/dist/simulate.rc b/dist/simulate.rc index 68633346..df22c091 100644 --- a/dist/simulate.rc +++ b/dist/simulate.rc @@ -1,8 +1,8 @@ MUJOCO ICON "mujoco.ico" 1 VERSIONINFO -FILEVERSION 3,1,6,0 -PRODUCTVERSION 3,1,6,0 +FILEVERSION 3,1,7,0 +PRODUCTVERSION 3,1,7,0 FILEOS 0x4 FILETYPE 0x1 { @@ -11,9 +11,9 @@ FILETYPE 0x1 BLOCK "040904b0" { VALUE "ProductName", "MuJoCo" - VALUE "ProductVersion", "3.1.6" + VALUE "ProductVersion", "3.1.7" VALUE "FileDescription", "MuJoCo" - VALUE "FileVersion", "3.1.6" + VALUE "FileVersion", "3.1.7" VALUE "InternalName", "simulate.exe" VALUE "OriginalFilename", "simulate.exe" VALUE "CompanyName", "Google DeepMind" diff --git a/doc/APIreference/APIglobals.rst b/doc/APIreference/APIglobals.rst index 9f534b5a..2f4e0989 100644 --- a/doc/APIreference/APIglobals.rst +++ b/doc/APIreference/APIglobals.rst @@ -522,7 +522,7 @@ shown in the table below. Their names are in the format ``mjKEY_XXX``. They corr - Maximum number of UI rectangles. Defined in `mjui.h `_. * - ``mjVERSION_HEADER`` - - 316 + - 317 - The version of the MuJoCo headers; changes with every release. This is an integer equal to 100x the software version, so 210 corresponds to version 2.1. Defined in mujoco.h. The API function :ref:`mj_version` returns a number with the same meaning but for the compiled library. diff --git a/doc/unity.rst b/doc/unity.rst index 496d9d3f..dcd57f64 100644 --- a/doc/unity.rst +++ b/doc/unity.rst @@ -30,14 +30,14 @@ _____ The MuJoCo app needs to be run at least once before the native library can be used, in order to register the library as a trusted binary. Then, copy the dynamic library file from -``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.1.6.dylib`` (it can be +``/Applications/MuJoCo.app/Contents/Frameworks/mujoco.framework/Versions/Current/libmujoco.3.1.7.dylib`` (it can be found by browsing the contents of ``MuJoCo.app``) and rename it as ``mujoco.dylib``. Linux _____ Expand the ``tar.gz`` archive to ``~/.mujoco``. Then copy the dynamic library from -``~/.mujoco/mujoco-3.1.6/lib/libmujoco.so.3.1.6`` and rename it as ``libmujoco.so``. +``~/.mujoco/mujoco-3.1.7/lib/libmujoco.so.3.1.7`` and rename it as ``libmujoco.so``. Windows _______ diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index a6506395..db0f1386 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -24,7 +24,7 @@ extern "C" { #endif // header version; should match the library version as returned by mj_version() -#define mjVERSION_HEADER 316 +#define mjVERSION_HEADER 317 // needed to define size_t, fabs and log10 #include diff --git a/mjx/pyproject.toml b/mjx/pyproject.toml index f4fd5783..0a9a1637 100644 --- a/mjx/pyproject.toml +++ b/mjx/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name="mujoco-mjx" -version = "3.1.6" +version = "3.1.7" authors = [ {name = "Google DeepMind", email = "mujoco@deepmind.com"}, ] @@ -31,7 +31,7 @@ dependencies = [ "etils[epath]", "jax", "jaxlib", - "mujoco>=3.1.6.dev0", + "mujoco>=3.1.7.dev0", "scipy", "trimesh", ] @@ -42,6 +42,6 @@ mjx-viewer = "mujoco.mjx.viewer:main" [project.urls] Homepage = "https://github.com/google-deepmind/mujoco/tree/main/mjx" -Documentation = "https://mujoco.readthedocs.io/en/3.1.6" +Documentation = "https://mujoco.readthedocs.io/en/3.1.7" Repository = "https://github.com/google-deepmind/mujoco/tree/main/mjx" -Changelog = "https://mujoco.readthedocs.io/en/3.1.6/changelog.html" +Changelog = "https://mujoco.readthedocs.io/en/3.1.7/changelog.html" diff --git a/python/mujoco/CMakeLists.txt b/python/mujoco/CMakeLists.txt index 21ee692c..d31bd903 100644 --- a/python/mujoco/CMakeLists.txt +++ b/python/mujoco/CMakeLists.txt @@ -84,7 +84,7 @@ if(NOT TARGET mujoco) if(MUJOCO_FRAMEWORK) message("MuJoCo framework is at ${MUJOCO_FRAMEWORK}/mujoco.framework") set(MUJOCO_LIBRARY - ${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.1.6.dylib + ${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.1.7.dylib ) target_compile_options(mujoco INTERFACE -F${MUJOCO_FRAMEWORK}) endif() @@ -92,7 +92,7 @@ if(NOT TARGET mujoco) if(NOT MUJOCO_FRAMEWORK) find_library( - MUJOCO_LIBRARY mujoco mujoco.3.1.6 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED + MUJOCO_LIBRARY mujoco mujoco.3.1.7 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED ) find_path(MUJOCO_INCLUDE mujoco/mujoco.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED) message("MuJoCo is at ${MUJOCO_LIBRARY}") diff --git a/python/mujoco/mjpython/Info.plist b/python/mujoco/mjpython/Info.plist index c1bfaaed..c7e6c6b3 100644 --- a/python/mujoco/mjpython/Info.plist +++ b/python/mujoco/mjpython/Info.plist @@ -7,13 +7,13 @@ CFBundleIdentifier org.mujoco.mjpython CFBundleVersion - 3.1.6 + 3.1.7 CFBundleGetInfoString - 3.1.6 + 3.1.7 CFBundleLongVersionString - 3.1.6 + 3.1.7 CFBundleShortVersionString - 3.1.6 + 3.1.7 CFBundleExecutable mjpython CFBundleIconFile diff --git a/python/pyproject.toml b/python/pyproject.toml index 8d134c8e..775fdb91 100644 --- a/python/pyproject.toml +++ b/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mujoco" -version = "3.1.6" +version = "3.1.7" authors = [ {name = "Google DeepMind", email = "mujoco@deepmind.com"}, ] @@ -36,9 +36,9 @@ dynamic = ["readme", "scripts"] [project.urls] Homepage = "https://github.com/google-deepmind/mujoco" -Documentation = "https://mujoco.readthedocs.io/en/3.1.6" +Documentation = "https://mujoco.readthedocs.io/en/3.1.7" Repository = "https://github.com/google-deepmind/mujoco" -Changelog = "https://mujoco.readthedocs.io/en/3.1.6/changelog.html" +Changelog = "https://mujoco.readthedocs.io/en/3.1.7/changelog.html" [tool.setuptools] include-package-data = false diff --git a/sample/CMakeLists.txt b/sample/CMakeLists.txt index 6924e0a0..e11c3937 100644 --- a/sample/CMakeLists.txt +++ b/sample/CMakeLists.txt @@ -24,7 +24,7 @@ set(MSVC_INCREMENTAL_DEFAULT ON) project( mujoco_samples - VERSION 3.1.6 + VERSION 3.1.7 DESCRIPTION "MuJoCo samples binaries" HOMEPAGE_URL "https://mujoco.org" ) diff --git a/simulate/CMakeLists.txt b/simulate/CMakeLists.txt index 7085897b..a487f0b8 100644 --- a/simulate/CMakeLists.txt +++ b/simulate/CMakeLists.txt @@ -29,7 +29,7 @@ set(MUJOCO_DEP_VERSION_lodepng project( mujoco_simulate - VERSION 3.1.6 + VERSION 3.1.7 DESCRIPTION "MuJoCo simulate binaries" HOMEPAGE_URL "https://mujoco.org" ) diff --git a/src/engine/engine_support.c b/src/engine/engine_support.c index 1b3ee7a3..e2d85b10 100644 --- a/src/engine/engine_support.c +++ b/src/engine/engine_support.c @@ -39,8 +39,8 @@ //-------------------------- Constants ------------------------------------------------------------- - #define mjVERSION 316 -#define mjVERSIONSTRING "3.1.6" + #define mjVERSION 317 +#define mjVERSIONSTRING "3.1.7" // names of disable flags const char* mjDISABLESTRING[mjNDISABLE] = { diff --git a/unity/Editor/Bindings/MujocoBinaryRetriever.cs b/unity/Editor/Bindings/MujocoBinaryRetriever.cs index 2a5d3568..f2b885bd 100644 --- a/unity/Editor/Bindings/MujocoBinaryRetriever.cs +++ b/unity/Editor/Bindings/MujocoBinaryRetriever.cs @@ -37,7 +37,7 @@ public class MujocoBinaryRetriever { if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/mujoco.dylib") == null) { File.Copy( "/Applications/MuJoCo.app/Contents/Frameworks" + - "/mujoco.framework/Versions/Current/libmujoco.3.1.6.dylib", + "/mujoco.framework/Versions/Current/libmujoco.3.1.7.dylib", mujocoPath + "/mujoco.dylib"); AssetDatabase.Refresh(); } @@ -45,7 +45,7 @@ public class MujocoBinaryRetriever { if (AssetDatabase.LoadMainAssetAtPath(mujocoPath + "/libmujoco.so") == null) { File.Copy( Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + - "/.mujoco/mujoco-3.1.6/lib/libmujoco.so.3.1.6", + "/.mujoco/mujoco-3.1.7/lib/libmujoco.so.3.1.7", mujocoPath + "/libmujoco.so"); AssetDatabase.Refresh(); } diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index ca8a3729..d81ebb90 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -108,7 +108,7 @@ public const int mjMAXLINEPNT = 1000; public const int mjMAXPLANEGRID = 200; public const bool THIRD_PARTY_MUJOCO_MJXMACRO_H_ = true; public const bool THIRD_PARTY_MUJOCO_MUJOCO_H_ = true; -public const int mjVERSION_HEADER = 316; +public const int mjVERSION_HEADER = 317; // ------------------------------------Enums------------------------------------ diff --git a/unity/package.json b/unity/package.json index a4c0308a..e33895d9 100644 --- a/unity/package.json +++ b/unity/package.json @@ -1,7 +1,7 @@ { "name": "org.mujoco", "displayName": "MuJoCo", - "version": "3.1.6", + "version": "3.1.7", "description": "MuJoCo importer and runtime plug-in", "dependencies": {}, "author": { From b35ae973e19e1793963c5d15571c7adafa640d63 Mon Sep 17 00:00:00 2001 From: Alessio Quaglino Date: Tue, 4 Jun 2024 11:59:32 +0100 Subject: [PATCH 05/32] Add mj_parseXML and mj_saveXML to xml_api. PiperOrigin-RevId: 640099466 Change-Id: I75f763523c5bc8f6f7c2f2de6806d82ba37ebd4e --- src/xml/xml.cc | 6 +-- src/xml/xml.h | 7 ++- src/xml/xml_api.cc | 50 +++++++++++++++++++ src/xml/xml_api.h | 9 ++++ src/xml/xml_base.cc | 4 +- src/xml/xml_base.h | 2 +- src/xml/xml_native_writer.cc | 2 +- src/xml/xml_native_writer.h | 2 +- test/user/user_api_test.cc | 16 +++--- test/xml/xml_api_test.cc | 78 ++++++++++++++++++++++++------ test/xml/xml_native_reader_test.cc | 4 +- 11 files changed, 143 insertions(+), 37 deletions(-) diff --git a/src/xml/xml.cc b/src/xml/xml.cc index dfd60c61..ad1cf06f 100644 --- a/src/xml/xml.cc +++ b/src/xml/xml.cc @@ -100,7 +100,7 @@ class LocaleOverride { } // namespace // Main writer function - calls mjXWrite -std::string mjWriteXML(mjSpec* spec, char* error, int error_sz) { +std::string mjWriteXML(const mjSpec* spec, char* error, int error_sz) { LocaleOverride locale_override; // check for empty model @@ -424,9 +424,9 @@ static void RegisterResourceProvider() { mjSpec* ParseSpecFromString(std::string_view xml, char* error, - int error_size, mjVFS* vfs) { + int error_size) { RegisterResourceProvider(); std::string xml2 = {xml.begin(), xml.end()}; std::string str = "LoadModelFromString:" + xml2; - return mjParseXML(str.c_str(), vfs, error, error_size); + return mjParseXML(str.c_str(), nullptr, error, error_size); } diff --git a/src/xml/xml.h b/src/xml/xml.h index 10b5e4a3..2fdd3acc 100644 --- a/src/xml/xml.h +++ b/src/xml/xml.h @@ -24,15 +24,14 @@ // Top level API // Main writer function -std::string mjWriteXML(mjSpec* spec, char* error, int error_sz); +std::string mjWriteXML(const mjSpec* spec, char* error, int error_sz); // Main parser function -MJAPI mjSpec* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); +mjSpec* mjParseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); // Returns a newly-allocated mjSpec, loaded from the contents of xml. // On failure returns nullptr and populates the error array if present. -MJAPI mjSpec* ParseSpecFromString(std::string_view xml, char* error = nullptr, - int error_size = 0, mjVFS* vfs = nullptr); +mjSpec* ParseSpecFromString(std::string_view xml, char* error = nullptr, int error_size = 0); #endif // MUJOCO_SRC_XML_XML_H_ diff --git a/src/xml/xml_api.cc b/src/xml/xml_api.cc index 1f30e8a5..23af4c81 100644 --- a/src/xml/xml_api.cc +++ b/src/xml/xml_api.cc @@ -209,3 +209,53 @@ mjModel* mj_loadModel(const char* filename, const mjVFS* vfs) { return m; } + + +// parse spec from file +mjSpec* mj_parseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz) { + return mjParseXML(filename, vfs, error, error_sz); +} + + + +// parse spec from string +mjSpec* mj_parseXMLString(const char* xml, const mjVFS* vfs, char* error, int error_sz) { + return ParseSpecFromString(xml, error, error_sz); +} + + + +// save spec to XML file, return 1 on success, 0 otherwise +int mj_saveXML(const mjSpec* s, const char* filename, char* error, int error_sz) { + std::string result = mjWriteXML(s, error, error_sz); + if (result.empty()) { + return 0; + } + + std::ofstream file; + file.open(filename); + file << result; + file.close(); + return 1; +} + + + +// save spec to string, return 1 on success, 0 otherwise +int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int error_sz) { + std::string result = mjWriteXML(s, error, error_sz); + if (result.size() >= xml_sz) { + std::string error_msg = "Output string too short, should be at least " + + std::to_string(result.size()+1); + mjCopyError(error, error_msg.c_str(), error_sz); + return 0; + } + if (result.empty()) { + return 0; + } + + result.copy(xml, xml_sz); + xml[result.size()] = 0; + return 1; +} + diff --git a/src/xml/xml_api.h b/src/xml/xml_api.h index bc2a5a71..046e8410 100644 --- a/src/xml/xml_api.h +++ b/src/xml/xml_api.h @@ -17,6 +17,7 @@ #include #include +#include "user/user_api.h" #ifdef __cplusplus extern "C" { @@ -43,6 +44,14 @@ MJAPI int mj_printSchema(const char* filename, char* buffer, int buffer_sz, // if vfs is not NULL, look up file in vfs before reading from disk MJAPI mjModel* mj_loadModel(const char* filename, const mjVFS* vfs); +// parse spec from file or XML string. +MJAPI mjSpec* mj_parseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); +MJAPI mjSpec* mj_parseXMLString(const char* xml, const mjVFS* vfs, char* error, int error_sz); + +// Save spec to XML file and/or string, return 1 on success, 0 otherwise. +MJAPI int mj_saveXML(const mjSpec* s, const char* filename, char* error, int error_sz); +MJAPI int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int error_sz); + #ifdef __cplusplus } #endif diff --git a/src/xml/xml_base.cc b/src/xml/xml_base.cc index 307dc69e..bcefd583 100644 --- a/src/xml/xml_base.cc +++ b/src/xml/xml_base.cc @@ -45,8 +45,8 @@ mjXBase::mjXBase() { // set model field -void mjXBase::SetModel(mjSpec* _model) { - model = _model; +void mjXBase::SetModel(const mjSpec* _model) { + model = (mjSpec*)_model; } diff --git a/src/xml/xml_base.h b/src/xml/xml_base.h index 26d95fcb..7df966cc 100644 --- a/src/xml/xml_base.h +++ b/src/xml/xml_base.h @@ -87,7 +87,7 @@ class mjXBase : public mjXUtil { }; // set the model allocated externally - virtual void SetModel(mjSpec*); + virtual void SetModel(const mjSpec*); // read alternative orientation specification static int ReadAlternative(tinyxml2::XMLElement* elem, mjsOrientation& alt); diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index 97daccd4..c9012bdd 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -769,7 +769,7 @@ mjXWriter::mjXWriter(void) { // cast model -void mjXWriter::SetModel(mjSpec* spec) { +void mjXWriter::SetModel(const mjSpec* spec) { if (spec) { model = (mjCModel*)spec->element; } diff --git a/src/xml/xml_native_writer.h b/src/xml/xml_native_writer.h index 063fddca..6f5f0812 100644 --- a/src/xml/xml_native_writer.h +++ b/src/xml/xml_native_writer.h @@ -27,7 +27,7 @@ class mjXWriter : public mjXBase { public: mjXWriter(); // constructor virtual ~mjXWriter() = default; // destructor - void SetModel(mjSpec* spec); + void SetModel(const mjSpec* spec); // write XML document to string std::string Write(char *error, std::size_t error_sz); diff --git a/test/user/user_api_test.cc b/test/user/user_api_test.cc index 440f0c96..e6b63761 100644 --- a/test/user/user_api_test.cc +++ b/test/user/user_api_test.cc @@ -25,7 +25,7 @@ #include #include #include "src/user/user_api.h" -#include "src/xml/xml.h" +#include "src/xml/xml_api.h" #include "src/xml/xml_numeric_format.h" #include "test/fixture.h" @@ -125,7 +125,7 @@ TEST_F(PluginTest, RecompileCompare) { // load spec std::array err; - mjSpec* s = mjParseXML(xml.c_str(), nullptr, err.data(), err.size()); + mjSpec* s = mj_parseXML(xml.c_str(), 0, err.data(), err.size()); ASSERT_THAT(s, NotNull()) << "Failed to load " << xml << ": " << err.data(); @@ -417,7 +417,7 @@ TEST_F(MujocoTest, AttachSame) { )"; // create parent - mjSpec* parent = ParseSpecFromString(xml_child, er.data(), er.size()); + mjSpec* parent = mj_parseXMLString(xml_child, 0, er.data(), er.size()); EXPECT_THAT(parent, NotNull()) << er.data(); // get frame @@ -530,7 +530,7 @@ TEST_F(MujocoTest, AttachDifferent) { )"; // model with one free sphere and a frame - mjSpec* parent = ParseSpecFromString(xml_parent, er.data(), er.size()); + mjSpec* parent = mj_parseXMLString(xml_parent, 0, er.data(), er.size()); EXPECT_THAT(parent, NotNull()) << er.data(); // get frame @@ -538,7 +538,7 @@ TEST_F(MujocoTest, AttachDifferent) { EXPECT_THAT(frame, NotNull()); // model with one cylinder and a hinge - mjSpec* child = ParseSpecFromString(xml_child, er.data(), er.size()); + mjSpec* child = mj_parseXMLString(xml_child, 0, er.data(), er.size()); EXPECT_THAT(child, NotNull()) << er.data(); // get subtree @@ -642,7 +642,7 @@ TEST_F(MujocoTest, AttachFrame) { )"; // model with one free sphere and a frame - mjSpec* parent = ParseSpecFromString(xml_parent, er.data(), er.size()); + mjSpec* parent = mj_parseXMLString(xml_parent, 0, er.data(), er.size()); EXPECT_THAT(parent, NotNull()) << er.data(); // get frame @@ -650,7 +650,7 @@ TEST_F(MujocoTest, AttachFrame) { EXPECT_THAT(body, NotNull()); // model with one cylinder and a hinge - mjSpec* child = ParseSpecFromString(xml_child, er.data(), er.size()); + mjSpec* child = mj_parseXMLString(xml_child, 0, er.data(), er.size()); EXPECT_THAT(child, NotNull()) << er.data(); // get subtree @@ -711,7 +711,7 @@ void TestDetachBody(bool compile) { )"; // model with one cylinder and a hinge - mjSpec* child = ParseSpecFromString(xml_child, er.data(), er.size()); + mjSpec* child = mj_parseXMLString(xml_child, 0, er.data(), er.size()); EXPECT_THAT(child, NotNull()) << er.data(); // compile model (for testing double compilation) diff --git a/test/xml/xml_api_test.cc b/test/xml/xml_api_test.cc index 8988b851..d11be846 100644 --- a/test/xml/xml_api_test.cc +++ b/test/xml/xml_api_test.cc @@ -24,6 +24,8 @@ #include #include #include +#include "src/user/user_api.h" +#include "src/xml/xml_api.h" #include "test/fixture.h" namespace mujoco { @@ -33,6 +35,21 @@ using ::testing::IsNull; using ::testing::NotNull; using ::testing::StartsWith; +static constexpr char xml[] = R"( + + + + + + + + + + + + + )"; + // ---------------------------- test mj_loadXML -------------------------------- using LoadXmlTest = MujocoTest; @@ -66,20 +83,6 @@ TEST_F(LoadXmlTest, InvalidXmlFailsToLoad) { } TEST_F(LoadXmlTest, MultipleBodies) { - static constexpr char xml[] = R"( - - - - - - - - - - - - - )"; std::array error; mjModel* model = LoadModelFromString(xml, error.data(), error.size()); @@ -92,7 +95,6 @@ TEST_F(LoadXmlTest, MultipleBodies) { mj_deleteData(data); mj_deleteModel(model); } - using SaveLastXmlTest = MujocoTest; TEST_F(SaveLastXmlTest, EmptyModel) { @@ -112,5 +114,51 @@ TEST_F(SaveLastXmlTest, EmptyModel) { mj_deleteModel(model); } +TEST_F(MujocoTest, SaveXmlShortString) { + std::array error; + + mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); + EXPECT_THAT(spec, NotNull()) << "Failed to parse spec: " << error.data(); + mjModel* model = mjs_compile(spec, 0); + EXPECT_THAT(model, NotNull()) << "Failed to compile model: " << error.data(); + + std::array out; + EXPECT_THAT(mj_saveXMLString(spec, out.data(), out.size(), + error.data(), error.size()), 0); + EXPECT_STREQ(error.data(), "Output string too short, should be at least 273"); + + mjs_deleteSpec(spec); + mj_deleteModel(model); +} + +TEST_F(MujocoTest, SaveXml) { + std::array error; + + mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); + EXPECT_THAT(spec, NotNull()) << "Failed to parse spec: " << error.data(); + mjModel* model = mjs_compile(spec, 0); + EXPECT_THAT(model, NotNull()) << "Failed to compile model: " << error.data(); + + std::array out; + EXPECT_THAT(mj_saveXMLString(spec, out.data(), out.size(), error.data(), + error.size()), 1) << error.data(); + + mjSpec* saved_spec = mj_parseXMLString(xml, 0, error.data(), error.size()); + EXPECT_THAT(saved_spec, NotNull()) << "Invalid saved spec: " << error.data(); + mjModel* saved_model = mjs_compile(saved_spec, 0); + EXPECT_THAT(saved_model, NotNull()) << "Invalid model: " << error.data(); + + mjtNum tol = 0; + std::string field = ""; + EXPECT_LE(CompareModel(model, saved_model, field), tol) + << "Expected and attached models are different!\n" + << "Different field: " << field << '\n'; + + mjs_deleteSpec(spec); + mjs_deleteSpec(saved_spec); + mj_deleteModel(model); + mj_deleteModel(saved_model); +} + } // namespace } // namespace mujoco diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index 1f225fa4..c07c8e73 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -28,7 +28,7 @@ #include "src/cc/array_safety.h" #include "src/engine/engine_util_errmem.h" #include "src/user/user_api.h" -#include "src/xml/xml.h" +#include "src/xml/xml_api.h" #include "test/fixture.h" namespace mujoco { @@ -1084,7 +1084,7 @@ TEST_F(XMLReaderTest, ParseReplicateDefaultPropagate) { )"; std::array error; - mjSpec* spec = ParseSpecFromString(xml, error.data(), error.size()); + mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); EXPECT_THAT(spec, NotNull()) << error.data(); mjsBody* torso = mjs_findBody(spec, "torso-0"); From 18397e61ecbc23f817229e909094d536f5705f7f Mon Sep 17 00:00:00 2001 From: Kyle Bayes Date: Tue, 4 Jun 2024 08:49:18 -0700 Subject: [PATCH 06/32] Replace calls to mj_makeEmptyFileVFS with mj_addBufferVFS in MuJoCo Unity bindings. PiperOrigin-RevId: 640168011 Change-Id: Ic592b5cbedb89981ac24d4b8d2c864ceaf415e46 --- unity/Runtime/Tools/MjVfs.cs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/unity/Runtime/Tools/MjVfs.cs b/unity/Runtime/Tools/MjVfs.cs index f46a9a88..a52da4b2 100644 --- a/unity/Runtime/Tools/MjVfs.cs +++ b/unity/Runtime/Tools/MjVfs.cs @@ -47,17 +47,16 @@ public sealed class MjVfs : IDisposable { // Adds a new file to the virtual filesystem. public unsafe void AddFile(string filename, string contents) { - var result = mj_makeEmptyFileVFS(_unmanagedVfs.ToPointer(), filename, contents.Length); - if (result != 0) { - throw new Exception( - "VFS error (" + result + ") encountered while creating an empty file"); - } - var fileIndex = mj_findFileVFS(_unmanagedVfs.ToPointer(), filename); - if (fileIndex < 0) { - throw new IndexOutOfRangeException("VFS didn't properly create the empty file."); - } var contents_bytes = Encoding.UTF8.GetBytes(contents); - Marshal.Copy(contents_bytes, 0, Data.filedata[fileIndex], contents_bytes.Length); + fixed (byte* bytes = contents_bytes) + { + IntPtr ptr = (IntPtr) bytes; + var result = mj_addBufferVFS(_unmanagedVfs.ToPointer(), filename, ptr.ToPointer(), + contents_bytes.Length); + if (result != 0) { + throw new Exception("VFS error (" + result + ") encountered while creating an empty file"); + } + } } // Searches the VFS for the specified file and returns its index. From 4debe1344a793944207231dd266163c9ddffd918 Mon Sep 17 00:00:00 2001 From: Kevin Zakka Date: Tue, 4 Jun 2024 12:08:28 -0700 Subject: [PATCH 07/32] Add WidowX 250 6-DoF. Closes #28. PiperOrigin-RevId: 640239380 Change-Id: I94c126f9d4f8711b6f8ab7d9b7cc1dace864a153 --- doc/models.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/models.rst b/doc/models.rst index 54cdf4a9..9ba7e806 100644 --- a/doc/models.rst +++ b/doc/models.rst @@ -120,6 +120,8 @@ Arms - .. image:: https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/ufactory_lite6/lite6.png * - `ViperX 300 6DOF `_ - .. image:: https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/trossen_vx300s/vx300s.png + * - `WidowX 250 6DOF `_ + - .. image:: https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/trossen_wx250s/wx250s.png * - `ALOHA 2 `_ - .. image:: https://raw.githubusercontent.com/google-deepmind/mujoco_menagerie/main/aloha/aloha.png * - `Unitree Z1 `_ From a56b9c8bbd328c0a573323c778f0a2da306a71a1 Mon Sep 17 00:00:00 2001 From: Bogdan Graur Date: Wed, 5 Jun 2024 04:30:25 -0700 Subject: [PATCH 08/32] Roll back workaround for upcoming libc++ bug which has since been fixed upstream. PiperOrigin-RevId: 640479545 Change-Id: I8a3700ef0b52f5364dcd0903aba196293a96c220 --- src/xml/xml_util.cc | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/xml/xml_util.cc b/src/xml/xml_util.cc index 651e3586..eec0dd39 100644 --- a/src/xml/xml_util.cc +++ b/src/xml/xml_util.cc @@ -481,16 +481,6 @@ bool mjXUtil::ReadAttrValues(XMLElement* elem, const char* attr, // read numbers for (int i = 0; (max < 0 || i < max) && !strm.eof(); ++i) { strm >> token; - - // some C++ libraries do not allow .x instead of 0.x - if (!token.empty()) { - if (token[0] == std::string(".")[0]) { - token = "0" + token; - } else if (token[0] == std::string("-")[0] && token[1] == std::string(".")[0]) { - token.insert(1, "0"); - } - } - std::istringstream token_strm(token); token_strm >> item; if (token_strm.fail() || !token_strm.eof()) { From 40755e165d692d23afa62078d234fa638f0b1fcc Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Wed, 5 Jun 2024 06:26:12 -0700 Subject: [PATCH 09/32] Reduce VeryLargeMemory test memory allocation in engine_io_test.cc An upcoming LLVM update is due to make this test fail under msan, since msan touches most of the memory allocated for the mjData arena. This causes an OOM failure on memory-limited testing infrastructure. An 8G allocation is sufficient to catch errors that arise from the use of 32-bit integers to handle sizes. PiperOrigin-RevId: 640504620 Change-Id: Ia1949b04a1428f01e1075e26b421facb3babedb6 --- test/engine/engine_io_test.cc | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/engine/engine_io_test.cc b/test/engine/engine_io_test.cc index 6419c133..fc38adc7 100644 --- a/test/engine/engine_io_test.cc +++ b/test/engine/engine_io_test.cc @@ -701,30 +701,30 @@ TEST_F(EngineIoTest, LargeMemory) { TEST_F(EngineIoTest, VeryLargeMemory) { constexpr char xml[] = R"( - + )"; std::array error; mjModel* model = LoadModelFromString(xml, error.data(), error.size()); if (!model) { - // in some test environments, 64GB is too large + // in some test environments, 8GB is too large EXPECT_THAT(error.data(), HasSubstr("Could not allocate memory")); } else { ASSERT_THAT(model, NotNull()) << "Failed to load model: " << error.data(); mjData* data = mj_makeData(model); ASSERT_THAT(data, NotNull()); - // allocate 63G of mjtNums + // allocate 7G of mjtNums mj_markStack(data); - size_t num = 63000000000 / sizeof(mjtNum); + size_t num = 7516192768ull / sizeof(mjtNum); mjtNum* testNum = mj_stackAllocNum(data, num); testNum[num-1] = 1; mj_freeStack(data); - // allocate 63G of bytes + // allocate 7G of bytes mj_markStack(data); - num = 63000000000; + num = 7516192768ull; char* testByte = (char*) mj_stackAllocByte(data, num, alignof(char)); testByte[num-1] = 1; mj_freeStack(data); From ba8a2d4c8ad5ae74df8a96253225864d9ecad00e Mon Sep 17 00:00:00 2001 From: Alessio Quaglino Date: Thu, 6 Jun 2024 03:29:56 -0700 Subject: [PATCH 10/32] Dynamic model editing. PiperOrigin-RevId: 640838852 Change-Id: I559b1d60f2a3a2d866788d4006be31757454c619 --- src/engine/engine_io.c | 6 +- src/engine/engine_io.h | 3 + src/user/user_api.cc | 10 ++++ src/user/user_api.h | 4 ++ src/user/user_model.cc | 83 ++++++++++++++++++++++++-- src/user/user_model.h | 10 ++-- src/user/user_objects.cc | 7 +++ src/user/user_objects.h | 10 +++- test/user/user_api_test.cc | 119 +++++++++++++++++++++++++++++++++++++ 9 files changed, 239 insertions(+), 13 deletions(-) diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index ee836546..c41932eb 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -1093,7 +1093,7 @@ static void mj_setPtrData(const mjModel* m, mjData* d) { // initialize plugins, copy into d (required for deletion) -static void _initPlugin(const mjModel* m, mjData* d) { +void mj_initPlugin(const mjModel* m, mjData* d) { d->nplugin = m->nplugin; for (int i = 0; i < m->nplugin; ++i) { d->plugin[i] = m->plugin[i]; @@ -1203,7 +1203,7 @@ mjData* mj_makeData(const mjModel* m) { mjData* d = NULL; mj_makeRawData(&d, m); if (d) { - _initPlugin(m, d); + mj_initPlugin(m, d); mj_resetData(m, d); } return d; @@ -1219,7 +1219,7 @@ mjData* mj_copyData(mjData* dest, const mjModel* m, const mjData* src) { // allocate new data if needed if (!dest) { mj_makeRawData(&dest, m); - _initPlugin(m, dest); + mj_initPlugin(m, dest); } // check sizes diff --git a/src/engine/engine_io.h b/src/engine/engine_io.h index 1c733a8a..da4d6082 100644 --- a/src/engine/engine_io.h +++ b/src/engine/engine_io.h @@ -110,6 +110,9 @@ MJAPI void mj_resetDataKeyframe(const mjModel* m, mjData* d, int key); // mjData arena allocate MJAPI void* mj_arenaAllocByte(mjData* d, size_t bytes, size_t alignment); +// init plugins +MJAPI void mj_initPlugin(const mjModel* m, mjData* d); + #ifndef ADDRESS_SANITIZER // mjData mark stack frame diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 34cfa704..1c6384fc 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -83,6 +83,16 @@ mjModel* mjs_compile(mjSpec* s, const mjVFS* vfs) { +// recompile spec into existing model and data while preserving the state +void mjs_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d) { + mjCModel* modelC = static_cast(s->element); + modelC->SaveState(d); + modelC->Compile(vfs, &m); + modelC->RestoreState(m, &d); +} + + + // attach body to a frame of the parent int mjs_attachBody(mjsFrame* parent, const mjsBody* child, const char* prefix, const char* suffix) { diff --git a/src/user/user_api.h b/src/user/user_api.h index 3f6ae15e..8cb70d8f 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -754,6 +755,9 @@ MJAPI mjSpec* mjs_createSpec(void); // Compile spec to model. MJAPI mjModel* mjs_compile(mjSpec* s, const mjVFS* vfs); +// Recompile spec to model preserving the current state. +MJAPI void mjs_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d); + // Copy spec. MJAPI mjSpec* mjs_copySpec(const mjSpec* s); diff --git a/src/user/user_model.cc b/src/user/user_model.cc index 7ff79c2c..f2bcbe5a 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -1843,6 +1843,8 @@ void mjCModel::CopyTree(mjModel* m) { int jid = pj->id; // set joint fields + pj->qposadr_ = qposadr; + pj->dofadr_ = dofadr; m->jnt_type[jid] = pj->type; m->jnt_group[jid] = pj->group; m->jnt_limited[jid] = (mjtByte)pj->is_limited(); @@ -2584,6 +2586,8 @@ void mjCModel::CopyObjects(mjModel* m) { m->actuator_trnid[2*i+1] = pac->trnid[1]; m->actuator_actnum[i] = pac->actdim + pac->plugin_actdim; m->actuator_actadr[i] = m->actuator_actnum[i] ? adr : -1; + pac->actadr_ = m->actuator_actadr[i]; + pac->actnum_ = m->actuator_actnum[i]; adr += m->actuator_actnum[i]; m->actuator_group[i] = pac->group; m->actuator_ctrllimited[i] = (mjtByte)pac->is_ctrllimited(); @@ -2716,6 +2720,75 @@ void mjCModel::CopyObjects(mjModel* m) { +// save the current state +void mjCModel::SaveState(const mjData* d) { + for (auto joint : joints_) { + switch (joint->type) { + case mjJNT_FREE: + mju_copy(joint->qpos, d->qpos + joint->qposadr_, 7); + mju_copy(joint->qvel, d->qvel + joint->dofadr_, 6); + break; + case mjJNT_BALL: + mju_copy(joint->qpos, d->qpos + joint->qposadr_, 4); + mju_copy(joint->qvel, d->qvel + joint->dofadr_, 3); + break; + case mjJNT_HINGE: + case mjJNT_SLIDE: + mju_copy(joint->qpos, d->qpos + joint->qposadr_, 1); + mju_copy(joint->qvel, d->qvel + joint->dofadr_, 1); + break; + } + } + + for (auto actuator : actuators_) { + if (actuator->actadr_ != -1) { + actuator->act.assign(actuator->actnum_, 0); + mju_copy(actuator->act.data(), d->act + actuator->actadr_, actuator->actnum_); + } + } +} + + + +// restore the previous state +void mjCModel::RestoreState(const mjModel* m, mjData** dest) { + mj_makeRawData(dest, m); + mjData* d = *dest; + if (d) { + mj_initPlugin(m, d); + mj_resetData(m, d); + } + + for (auto joint : joints_) { + if (!mjuu_defined(joint->qpos[0]) || !mjuu_defined(joint->qvel[0])) { + continue; + } + switch (joint->type) { + case mjJNT_FREE: + mju_copy(d->qpos + joint->qposadr_, joint->qpos, 7); + mju_copy(d->qvel + joint->dofadr_, joint->qvel, 6); + break; + case mjJNT_BALL: + mju_copy(d->qpos + joint->qposadr_, joint->qpos, 4); + mju_copy(d->qvel + joint->dofadr_, joint->qvel, 3); + break; + case mjJNT_HINGE: + case mjJNT_SLIDE: + mju_copy(d->qpos + joint->qposadr_, joint->qpos, 1); + mju_copy(d->qvel + joint->dofadr_, joint->qvel, 1); + break; + } + } + + for (auto actuator : actuators_) { + if (mjuu_defined(actuator->act[0])) { + mju_copy(d->act + actuator->actadr_, actuator->act.data(), actuator->actnum_); + } + } +} + + + //------------------------------- FUSE STATIC ------------------------------------------------------ template @@ -3075,7 +3148,7 @@ static void warninghandler(const char* msg) { // compiler -mjModel* mjCModel::Compile(const mjVFS* vfs) { +mjModel* mjCModel::Compile(const mjVFS* vfs, mjModel** m) { if (compiled) { // clear kinematic tree for (int i=0; i(&m), *const_cast(&data), vfs); + TryCompile(*const_cast(&model), *const_cast(&data), vfs); } catch (mjCError err) { // deallocate everything allocated in Compile - mj_deleteModel(m); + mj_deleteModel(model); mj_deleteData(data); mjCBody* world = bodies_[0]; Clear(); @@ -3140,7 +3213,7 @@ mjModel* mjCModel::Compile(const mjVFS* vfs) { _mjPRIVATE__set_tls_error_fn(save_error); _mjPRIVATE__set_tls_warning_fn(save_warning); compiled = true; - return m; + return model; } diff --git a/src/user/user_model.h b/src/user/user_model.h index 135510af..95939964 100644 --- a/src/user/user_model.h +++ b/src/user/user_model.h @@ -168,7 +168,7 @@ class mjCModel : public mjCModel_, private mjSpec { mjSpec spec; - mjModel* Compile(const mjVFS* vfs = nullptr); // construct mjModel + mjModel* Compile(const mjVFS* vfs = nullptr, mjModel** m = nullptr); // construct mjModel bool CopyBack(const mjModel*); // DECOMPILER: copy numeric back void FuseStatic(); // fuse static bodies with parent void FuseReindex(mjCBody* body); // reindex elements during fuse @@ -254,9 +254,6 @@ class mjCModel : public mjCModel_, private mjSpec { const std::string& plugin_instance_name, mjCPlugin** plugin_instance); - void TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs); - mjModel* _Compile(const mjVFS* vfs); - // clear objects allocated by Compile void Clear(); @@ -267,6 +264,10 @@ class mjCModel : public mjCModel_, private mjSpec { template void DeleteMaterial(std::vector& list, std::string_view name = ""); + // save/restore the current state + void SaveState(const mjData* d); + void RestoreState(const mjModel* m, mjData** dest); + private: // settings for each defaults class std::vector defaults_; @@ -275,6 +276,7 @@ class mjCModel : public mjCModel_, private mjSpec { std::vector> active_plugins_; // compile phases + void TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs); void MakeLists(mjCBody* body); // make lists of bodies, geoms, joints, sites void SetNuser(); // set nuser fields void IndexAssets(bool discard); // convert asset names into indices diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index 034abae6..d59e4950 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -1733,6 +1733,10 @@ mjCJoint::mjCJoint(mjCModel* _model, mjCDef* _def) { // in case this joint is not compiled CopyFromSpec(); + + // no previous state when a joint is created + qpos[0] = mjNAN; + qvel[0] = mjNAN; } @@ -5003,6 +5007,9 @@ mjCActuator::mjCActuator(mjCModel* _model, mjCDef* _def) { // point to local PointToLocal(); + + // no previous state when an actuator is created + act.push_back(mjNAN); } diff --git a/src/user/user_objects.h b/src/user/user_objects.h index d5773515..8860a6c9 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -423,10 +423,14 @@ class mjCJoint : public mjCJoint_, private mjsJoint { bool is_limited() const; bool is_actfrclimited() const; - private: int Compile(void); // compiler; return dofnum void PointToLocal(void); + + int qposadr_; // address of dof in data->qpos + int dofadr_; // address of dof in data->qvel + mjtNum qpos[7]; // qpos at the previous step + mjtNum qvel[6]; // qvel at the previous step }; @@ -1412,6 +1416,10 @@ class mjCActuator : public mjCActuator_, private mjsActuator { void NameSpace(const mjCModel* m); mjCBase* ptarget; // transmission target + + int actadr_; // address of dof in data->act + int actnum_; // number of dofs in data->act + std::vector act; // act at the previous step }; diff --git a/test/user/user_api_test.cc b/test/user/user_api_test.cc index e6b63761..2bc661bc 100644 --- a/test/user/user_api_test.cc +++ b/test/user/user_api_test.cc @@ -747,5 +747,124 @@ TEST_F(MujocoTest, DetachBody) { TestDetachBody(/*compile=*/true); } +TEST_F(MujocoTest, PreserveState) { + std::array er; + std::string field = ""; + + static constexpr char xml_full[] = R"( + + + + + + + + + + + + + + + + )"; + + static constexpr char xml_expected[] = R"( + + + + + + + + + + + + + + + )"; + + // load spec + mjSpec* spec = mj_parseXMLString(xml_full, 0, er.data(), er.size()); + EXPECT_THAT(spec, NotNull()) << er.data(); + + // compile models + mjModel* model = mjs_compile(spec, 0); + EXPECT_THAT(model, NotNull()); + mjModel* m_expected = LoadModelFromString(xml_expected, er.data(), er.size()); + EXPECT_THAT(m_expected, NotNull()); + + // create data + mjData* data = mj_makeData(model); + EXPECT_THAT(data, NotNull()); + mjData* d_expected = mj_makeData(m_expected); + EXPECT_THAT(d_expected, NotNull()); + + // set ctrl + data->ctrl[0] = 1; + data->ctrl[1] = 2; + d_expected->ctrl[0] = 2; + + // step models + mj_step(model, data); + mj_step(m_expected, d_expected); + + // detach subtree + mjsBody* body = mjs_findBody(spec, "detachable"); + EXPECT_THAT(body, NotNull()); + EXPECT_THAT(mjs_detachBody(spec, body), 0); + + // add body + mjsBody* newbody = mjs_addBody(mjs_findBody(spec, "world"), 0); + EXPECT_THAT(newbody, NotNull()); + + // add geom and joint + mjsGeom* geom = mjs_addGeom(newbody, 0); + mjsJoint* joint = mjs_addJoint(newbody, 0); + + // set properties + newbody->pos[0] = 2; + geom->size[0] = .3; + joint->type = mjJNT_SLIDE; + joint->axis[0] = 0; + joint->axis[1] = 0; + joint->axis[2] = 1; + joint->ref = d_expected->qpos[m_expected->nq-1]; + + // compile new model + mjs_recompile(spec, 0, model, data); + EXPECT_THAT(model, NotNull()); + + // compare qpos + EXPECT_EQ(model->nq, m_expected->nq); + for (int i = 0; i < model->nq; ++i) { + EXPECT_EQ(data->qpos[i], d_expected->qpos[i]) << i; + } + + // compare qvel + EXPECT_EQ(model->nv, m_expected->nv); + for (int i = 0; i < model->nv-1; ++i) { + EXPECT_EQ(data->qvel[i], d_expected->qvel[i]) << i; + } + + // second body was added after stepping so qvel should be zero + EXPECT_EQ(data->qvel[model->nv-1], 0); + + // compare act + EXPECT_EQ(model->na, m_expected->na); + for (int i = 0; i < model->na; ++i) { + EXPECT_EQ(data->act[i], d_expected->act[i]) << i; + } + + // destroy everything + mj_deleteData(data); + mj_deleteData(d_expected); + mjs_deleteSpec(spec); + mj_deleteModel(model); + mj_deleteModel(m_expected); +} + } // namespace } // namespace mujoco From 9a140bad1434618937f2a1afabd7bd5e5d380328 Mon Sep 17 00:00:00 2001 From: Alessio Quaglino Date: Thu, 6 Jun 2024 05:43:26 -0700 Subject: [PATCH 11/32] Use suffix instead of prefix underscore for private members in `user_api.h`. PiperOrigin-RevId: 640866779 Change-Id: Ifab04d8cdfe7e4c416d1916eb66f7d3584259f30 --- src/user/user_api.h | 86 ++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/user/user_api.h b/src/user/user_api.h index 8cb70d8f..cc192cb3 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -33,24 +33,24 @@ extern "C" { //---------------------------------- handles to internal objects ----------------------------------- -typedef struct _mjString* mjString; -typedef struct _mjStringVec* mjStringVec; -typedef struct _mjIntVec* mjIntVec; -typedef struct _mjIntVecVec* mjIntVecVec; -typedef struct _mjFloatVec* mjFloatVec; -typedef struct _mjFloatVecVec* mjFloatVecVec; -typedef struct _mjDoubleVec* mjDoubleVec; +typedef struct mjString_* mjString; +typedef struct mjStringVec_* mjStringVec; +typedef struct mjIntVec_* mjIntVec; +typedef struct mjIntVecVec_* mjIntVecVec; +typedef struct mjFloatVec_* mjFloatVec; +typedef struct mjFloatVecVec_* mjFloatVecVec; +typedef struct mjDoubleVec_* mjDoubleVec; //---------------------------------- enum types (mjt) ---------------------------------------------- -typedef enum _mjtGeomInertia { // type of inertia inference +typedef enum mjtGeomInertia_ { // type of inertia inference mjINERTIA_VOLUME, // mass distributed in the volume mjINERTIA_SHELL, // mass distributed on the surface } mjtGeomInertia; -typedef enum _mjtBuiltin { // type of built-in procedural texture +typedef enum mjtBuiltin_ { // type of built-in procedural texture mjBUILTIN_NONE = 0, // no built-in texture mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2 mjBUILTIN_CHECKER, // checker pattern: rgb1, rgb2 @@ -58,7 +58,7 @@ typedef enum _mjtBuiltin { // type of built-in procedural texture } mjtBuiltin; -typedef enum _mjtMark { // mark type for procedural textures +typedef enum mjtMark_ { // mark type for procedural textures mjMARK_NONE = 0, // no mark mjMARK_EDGE, // edges mjMARK_CROSS, // cross @@ -66,21 +66,21 @@ typedef enum _mjtMark { // mark type for procedural textures } mjtMark; -typedef enum _mjtLimited { // type of limit specification +typedef enum mjtLimited_ { // type of limit specification mjLIMITED_FALSE = 0, // not limited mjLIMITED_TRUE, // limited mjLIMITED_AUTO, // limited inferred from presence of range } mjtLimited; -typedef enum _mjtInertiaFromGeom { // whether to infer body inertias from child geoms +typedef enum mjtInertiaFromGeom_ { // whether to infer body inertias from child geoms mjINERTIAFROMGEOM_FALSE = 0, // do not use; inertial element required mjINERTIAFROMGEOM_TRUE, // always use; overwrite inertial element mjINERTIAFROMGEOM_AUTO // use only if inertial element is missing } mjtInertiaFromGeom; -typedef enum _mjtOrientation { // type of orientation specifier +typedef enum mjtOrientation_ { // type of orientation specifier mjORIENTATION_QUAT = 0, // quaternion mjORIENTATION_AXISANGLE, // axis and angle mjORIENTATION_XYAXES, // x and y axes @@ -91,12 +91,12 @@ typedef enum _mjtOrientation { // type of orientation specifier //---------------------------------- attribute structs (mjs) --------------------------------------- -typedef struct _mjElement { // element type, do not modify +typedef struct mjElement_ { // element type, do not modify mjtObj elemtype; // element type } mjElement; -typedef struct _mjSpec { // model specification +typedef struct mjSpec_ { // model specification mjElement* element; // element type mjString modelname; // model name @@ -152,7 +152,7 @@ typedef struct _mjSpec { // model specification } mjSpec; -typedef struct _mjsOrientation { // alternative orientation specifiers +typedef struct mjsOrientation_ { // alternative orientation specifiers mjtOrientation type; // active orientation specifier double axisangle[4]; // axis and angle double xyaxes[6]; // x and y axes @@ -161,7 +161,7 @@ typedef struct _mjsOrientation { // alternative orientation specifiers } mjsOrientation; -typedef struct _mjsPlugin { // plugin specification +typedef struct mjsPlugin_ { // plugin specification mjElement* instance; // element type mjString name; // name mjString instance_name; // instance name @@ -171,7 +171,7 @@ typedef struct _mjsPlugin { // plugin specification } mjsPlugin; -typedef struct _mjsBody { // body specification +typedef struct mjsBody_ { // body specification mjElement* element; // element type mjString name; // name mjString childclass; // childclass name @@ -199,7 +199,7 @@ typedef struct _mjsBody { // body specification } mjsBody; -typedef struct _mjsFrame { // frame specification +typedef struct mjsFrame_ { // frame specification mjElement* element; // element type mjString name; // name mjString childclass; // childclass name @@ -210,7 +210,7 @@ typedef struct _mjsFrame { // frame specification } mjsFrame; -typedef struct _mjsJoint { // joint specification +typedef struct mjsJoint_ { // joint specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -250,7 +250,7 @@ typedef struct _mjsJoint { // joint specification } mjsJoint; -typedef struct _mjsGeom { // geom specification +typedef struct mjsGeom_ { // geom specification mjElement* element; // element type mjString name; // name mjString classname; // classname @@ -299,7 +299,7 @@ typedef struct _mjsGeom { // geom specification } mjsGeom; -typedef struct _mjsSite { // site specification +typedef struct mjsSite_ { // site specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -323,7 +323,7 @@ typedef struct _mjsSite { // site specification } mjsSite; -typedef struct _mjsCamera { // camera specification +typedef struct mjsCamera_ { // camera specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -352,7 +352,7 @@ typedef struct _mjsCamera { // camera specification } mjsCamera; -typedef struct _mjsLight { // light specification +typedef struct mjsLight_ { // light specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -380,7 +380,7 @@ typedef struct _mjsLight { // light specification } mjsLight; -typedef struct _mjsFlex { +typedef struct mjsFlex_ { mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -421,7 +421,7 @@ typedef struct _mjsFlex { } mjsFlex; -typedef struct _mjsMesh { // mesh specification +typedef struct mjsMesh_ { // mesh specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -442,7 +442,7 @@ typedef struct _mjsMesh { // mesh specification } mjsMesh; -typedef struct _mjsHField { // height field specification +typedef struct mjsHField_ { // height field specification mjElement* element; // element type mjString name; // name mjString content_type; // content type of file @@ -456,7 +456,7 @@ typedef struct _mjsHField { // height field specification -typedef struct _mjsSkin { // skin specification +typedef struct mjsSkin_ { // skin specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -483,7 +483,7 @@ typedef struct _mjsSkin { // skin specification } mjsSkin; -typedef struct _mjsTexture { // texture specification +typedef struct mjsTexture_ { // texture specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -517,7 +517,7 @@ typedef struct _mjsTexture { // texture specification } mjsTexture; -typedef struct _mjsMaterial { // material specification +typedef struct mjsMaterial_ { // material specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -535,7 +535,7 @@ typedef struct _mjsMaterial { // material specification } mjsMaterial; -typedef struct _mjsPair { +typedef struct mjsPair_ { mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -554,7 +554,7 @@ typedef struct _mjsPair { } mjsPair; -typedef struct _mjsExclude { +typedef struct mjsExclude_ { mjElement* element; // element type mjString name; // name mjString bodyname1; // name of geom 1 @@ -563,7 +563,7 @@ typedef struct _mjsExclude { } mjsExclude; -typedef struct _mjsEquality { // equality specification +typedef struct mjsEquality_ { // equality specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -578,7 +578,7 @@ typedef struct _mjsEquality { // equality specification } mjsEquality; -typedef struct _mjsTendon { // tendon specification +typedef struct mjsTendon_ { // tendon specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -610,13 +610,13 @@ typedef struct _mjsTendon { // tendon specification } mjsTendon; -typedef struct _mjsWrap { // wrapping object specification +typedef struct mjsWrap_ { // wrapping object specification mjElement* element; // element type mjString info; // message appended to errors } mjsWrap; -typedef struct _mjsActuator { // actuator specification +typedef struct mjsActuator_ { // actuator specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -660,7 +660,7 @@ typedef struct _mjsActuator { // actuator specification } mjsActuator; -typedef struct _mjsSensor { // sensor specification +typedef struct mjsSensor_ { // sensor specification mjElement* element; // element type mjString name; // name mjString classname; // class name @@ -688,7 +688,7 @@ typedef struct _mjsSensor { // sensor specification } mjsSensor; -typedef struct _mjsNumeric { // custom numeric field specification +typedef struct mjsNumeric_ { // custom numeric field specification mjElement* element; // element type mjString name; // name mjDoubleVec data; // initialization data @@ -697,7 +697,7 @@ typedef struct _mjsNumeric { // custom numeric field specification } mjsNumeric; -typedef struct _mjsText { // custom text specification +typedef struct mjsText_ { // custom text specification mjElement* element; // element type mjString name; // name mjString data; // text string @@ -705,7 +705,7 @@ typedef struct _mjsText { // custom text specification } mjsText; -typedef struct _mjsTuple { // tuple specification +typedef struct mjsTuple_ { // tuple specification mjElement* element; // element type mjString name; // name mjIntVec objtype; // object types @@ -715,7 +715,7 @@ typedef struct _mjsTuple { // tuple specification } mjsTuple; -typedef struct _mjsKey { // keyframe specification +typedef struct mjsKey_ { // keyframe specification mjElement* element; // element type mjString name; // name double time; // time @@ -729,7 +729,7 @@ typedef struct _mjsKey { // keyframe specification } mjsKey; -typedef struct _mjsDefault { // default specification +typedef struct mjsDefault_ { // default specification mjElement* element; // element type mjString name; // class name mjsJoint* joint; // joint defaults @@ -1070,7 +1070,7 @@ MJAPI void mjs_defaultPlugin(mjsPlugin* plugin); //---------------------------------- Compiler cache ------------------------------------------------ -typedef struct _mjCache* mjCache; +typedef struct mjCache_* mjCache; // Set the size of the cache in bytes. MJAPI void mj_setCacheSize(mjCache cache, size_t size); From f6084b4328eb45585badb8079d806dd37037f897 Mon Sep 17 00:00:00 2001 From: Alessio Quaglino Date: Thu, 6 Jun 2024 12:05:17 -0700 Subject: [PATCH 12/32] Change user_api handle types. PiperOrigin-RevId: 640978033 Change-Id: I53496671ac823b50e6f90ceee15bf3b314b06db8 --- src/user/user_api.cc | 66 +++----- src/user/user_api.h | 335 +++++++++++++++++++------------------ src/user/user_composite.cc | 4 +- src/user/user_flexcomp.cc | 4 +- src/user/user_mesh.cc | 112 ++++++------- src/user/user_model.cc | 20 +-- src/user/user_objects.cc | 310 +++++++++++++++++----------------- 7 files changed, 427 insertions(+), 424 deletions(-) diff --git a/src/user/user_api.cc b/src/user/user_api.cc index 1c6384fc..e99ea886 100644 --- a/src/user/user_api.cc +++ b/src/user/user_api.cc @@ -581,66 +581,61 @@ mjElement* mjs_nextChild(mjsBody* body, mjElement* child) { // set string -void mjs_setString(mjString dest, const char* text) { - std::string* str = reinterpret_cast(dest); +void mjs_setString(mjString* dest, const char* text) { + std::string* str = static_cast(dest); *str = std::string(text); } // Set specific entry in destination string vector. -mjtByte mjs_setInStringVec(mjStringVec dest, int i, const char* text) { - std::vector* v = reinterpret_cast*>(dest); - if (v->size() <= i) { +mjtByte mjs_setInStringVec(mjStringVec* dest, int i, const char* text) { + if (dest->size() <= i) { mju_error("Requested index in mjs_setInStringVec is out of bounds"); return 0; } - v->at(i) = std::string(text); + dest->at(i) = std::string(text); return 1; } // split text and copy into string array -void mjs_setStringVec(mjStringVec dest, const char* text) { - std::vector* v = reinterpret_cast*>(dest); +void mjs_setStringVec(mjStringVec* dest, const char* text) { + std::vector* v = static_cast*>(dest); *v = mjXUtil::String2Vector(text); } // add text entry to destination string vector -void mjs_appendString(mjStringVec dest, const char* text) { - std::vector* v = reinterpret_cast*>(dest); - v->push_back(std::string(text)); +void mjs_appendString(mjStringVec* dest, const char* text) { + dest->push_back(std::string(text)); } // copy int array to vector -void mjs_setInt(mjIntVec dest, const int* array, int size) { - std::vector* v = reinterpret_cast*>(dest); - v->assign(size, 0.0); +void mjs_setInt(mjIntVec* dest, const int* array, int size) { + dest->assign(size, 0.0); for (int i = 0; i < size; ++i) { - (*v)[i] = array[i]; + (*dest)[i] = array[i]; } } // append int array to vector of arrays -void mjs_appendIntVec(mjIntVecVec dest, const int* array, int size) { - std::vector>* v = reinterpret_cast>*>(dest); - v->push_back(std::vector(array, array + size)); +void mjs_appendIntVec(mjIntVecVec* dest, const int* array, int size) { + dest->push_back(std::vector(array, array + size)); } // copy float array to vector -void mjs_setFloat(mjFloatVec dest, const float* array, int size) { - std::vector* v = reinterpret_cast*>(dest); - v->assign(size, 0.0); +void mjs_setFloat(mjFloatVec* dest, const float* array, int size) { + dest->assign(size, 0.0); for (int i = 0; i < size; ++i) { - (*v)[i] = array[i]; + (*dest)[i] = array[i]; } } @@ -648,42 +643,35 @@ void mjs_setFloat(mjFloatVec dest, const float* array, int size) { // append float array to vector of arrays -void mjs_appendFloatVec(mjFloatVecVec dest, const float* array, int size) { - std::vector>* v = reinterpret_cast>*>(dest); - v->push_back(std::vector(array, array + size)); +void mjs_appendFloatVec(mjFloatVecVec* dest, const float* array, int size) { + dest->push_back(std::vector(array, array + size)); } // copy double array to vector -void mjs_setDouble(mjDoubleVec dest, const double* array, int size) { - std::vector* v = reinterpret_cast*>(dest); - v->assign(size, 0.0); +void mjs_setDouble(mjDoubleVec* dest, const double* array, int size) { + dest->assign(size, 0.0); for (int i = 0; i < size; ++i) { - (*v)[i] = array[i]; + (*dest)[i] = array[i]; } } // get string -const char* mjs_getString(const mjString source) { - std::string* str = reinterpret_cast(source); - if (!str) { - return nullptr; - } - return str->c_str(); +const char* mjs_getString(const mjString* source) { + return source->c_str(); } // get double array -const double* mjs_getDouble(const mjDoubleVec source, int* size) { - std::vector* v = reinterpret_cast*>(source); +const double* mjs_getDouble(const mjDoubleVec* source, int* size) { if (size) { - *size = v->size(); + *size = source->size(); } - return v->data(); + return source->data(); } diff --git a/src/user/user_api.h b/src/user/user_api.h index cc192cb3..96650d7e 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -25,21 +25,36 @@ // this is a C-API #ifdef __cplusplus +#include +#include + extern "C" { #endif #define mjNAN NAN // used to mark undefined fields -//---------------------------------- handles to internal objects ----------------------------------- +//---------------------------------- C/C++ handles to strings and arrays --------------------------- -typedef struct mjString_* mjString; -typedef struct mjStringVec_* mjStringVec; -typedef struct mjIntVec_* mjIntVec; -typedef struct mjIntVecVec_* mjIntVecVec; -typedef struct mjFloatVec_* mjFloatVec; -typedef struct mjFloatVecVec_* mjFloatVecVec; -typedef struct mjDoubleVec_* mjDoubleVec; +#ifdef __cplusplus + // C++, defined to be compatible with corresponding std types + using mjString = std::string; + using mjStringVec = std::vector; + using mjIntVec = std::vector; + using mjIntVecVec = std::vector>; + using mjFloatVec = std::vector; + using mjFloatVecVec = std::vector>; + using mjDoubleVec = std::vector; +#else + // C, opaque pointers + typedef struct mjString_ mjString; + typedef struct mjStringVec_ mjStringVec; + typedef struct mjIntVec_ mjIntVec; + typedef struct mjIntVecVec_ mjIntVecVec; + typedef struct mjFloatVec_ mjFloatVec; + typedef struct mjFloatVecVec_ mjFloatVecVec; + typedef struct mjDoubleVec_ mjDoubleVec; +#endif //---------------------------------- enum types (mjt) ---------------------------------------------- @@ -98,7 +113,7 @@ typedef struct mjElement_ { // element type, do not modify typedef struct mjSpec_ { // model specification mjElement* element; // element type - mjString modelname; // model name + mjString* modelname; // model name // compiler settings mjtByte autolimits; // infer "limited" attribute based on range @@ -110,8 +125,8 @@ typedef struct mjSpec_ { // model specification mjtByte fitaabb; // meshfit to aabb instead of inertia box mjtByte degree; // angles in radians or degrees char euler[3]; // sequence for euler rotations - mjString meshdir; // mesh and hfield directory - mjString texturedir; // texture directory + mjString* meshdir; // mesh and hfield directory + mjString* texturedir; // texture directory mjtByte discardvisual; // discard visual geoms in parser mjtByte convexhull; // compute mesh convex hulls mjtByte usethread; // use multiple threads to speed up compiler @@ -144,8 +159,8 @@ typedef struct mjSpec_ { // model specification size_t nstack; // (deprecated) number of mjtNums in mjData stack // global data - mjString comment; // comment at top of XML - mjString modelfiledir; // path to model file + mjString* comment; // comment at top of XML + mjString* modelfiledir; // path to model file // other mjtByte hasImplicitPluginElem; // already encountered an implicit plugin sensor/actuator @@ -163,18 +178,18 @@ typedef struct mjsOrientation_ { // alternative orientation specifiers typedef struct mjsPlugin_ { // plugin specification mjElement* instance; // element type - mjString name; // name - mjString instance_name; // instance name + mjString* name; // name + mjString* instance_name; // instance name int plugin_slot; // global registered slot number of the plugin mjtByte active; // is the plugin active - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsPlugin; typedef struct mjsBody_ { // body specification mjElement* element; // element type - mjString name; // name - mjString childclass; // childclass name + mjString* name; // name + mjString* childclass; // childclass name // body frame double pos[3]; // frame position @@ -192,28 +207,28 @@ typedef struct mjsBody_ { // body specification // other mjtByte mocap; // is this a mocap body double gravcomp; // gravity compensation - mjDoubleVec userdata; // user data + mjDoubleVec* userdata; // user data mjtByte explicitinertial; // whether to save the body with explicit inertial clause mjsPlugin plugin; // passive force plugin - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsBody; typedef struct mjsFrame_ { // frame specification mjElement* element; // element type - mjString name; // name - mjString childclass; // childclass name + mjString* name; // name + mjString* childclass; // childclass name double pos[3]; // position double quat[4]; // orientation mjsOrientation alt; // alternative orientation - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsFrame; typedef struct mjsJoint_ { // joint specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name + mjString* name; // name + mjString* classname; // class name mjtJoint type; // joint type // kinematics @@ -245,15 +260,15 @@ typedef struct mjsJoint_ { // joint specification // other int group; // group mjtByte actgravcomp; // is gravcomp force applied via actuators - mjDoubleVec userdata; // user data - mjString info; // message appended to compiler errors + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors } mjsJoint; typedef struct mjsGeom_ { // geom specification mjElement* element; // element type - mjString name; // name - mjString classname; // classname + mjString* name; // name + mjString* classname; // classname mjtGeom type; // geom type // frame, size @@ -285,24 +300,24 @@ typedef struct mjsGeom_ { // geom specification mjtNum fluid_coefs[5]; // ellipsoid-fluid interaction coefs // visual - mjString material; // name of material + mjString* material; // name of material float rgba[4]; // rgba when material is omitted int group; // group // other - mjString hfieldname; // heightfield attached to geom - mjString meshname; // mesh attached to geom + mjString* hfieldname; // heightfield attached to geom + mjString* meshname; // mesh attached to geom double fitscale; // scale mesh uniformly - mjDoubleVec userdata; // user data + mjDoubleVec* userdata; // user data mjsPlugin plugin; // sdf plugin - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsGeom; typedef struct mjsSite_ { // site specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name + mjString* name; // name + mjString* classname; // class name // frame, size double pos[3]; // position @@ -313,27 +328,27 @@ typedef struct mjsSite_ { // site specification // visual mjtGeom type; // geom type - mjString material; // name of material + mjString* material; // name of material int group; // group float rgba[4]; // rgba when material is omitted // other - mjDoubleVec userdata; // user data - mjString info; // message appended to compiler errors + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors } mjsSite; typedef struct mjsCamera_ { // camera specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name + mjString* name; // name + mjString* classname; // class name // extrinsics double pos[3]; // position double quat[4]; // orientation mjsOrientation alt; // alternative orientation mjtCamLight mode; // tracking mode - mjString targetbody; // target body for tracking/targeting + mjString* targetbody; // target body for tracking/targeting // intrinsics double fovy; // y-field of view @@ -347,21 +362,21 @@ typedef struct mjsCamera_ { // camera specification float principal_pixel[2]; // principal point (pixel) // other - mjDoubleVec userdata; // user data - mjString info; // message appended to compiler errors + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors } mjsCamera; typedef struct mjsLight_ { // light specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name + mjString* name; // name + mjString* classname; // class name // frame double pos[3]; // position double dir[3]; // direction mjtCamLight mode; // tracking mode - mjString targetbody; // target body for targeting + mjString* targetbody; // target body for targeting // intrinsics mjtByte active; // is light active @@ -376,14 +391,14 @@ typedef struct mjsLight_ { // light specification float specular[3]; // specular color // other - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errorsx } mjsLight; typedef struct mjsFlex_ { mjElement* element; // element type - mjString name; // name - mjString classname; // class name + mjString* name; // name + mjString* classname; // class name // contact properties int contype; // contact type @@ -408,85 +423,85 @@ typedef struct mjsFlex_ { double edgestiffness; // edge stiffness double edgedamping; // edge damping float rgba[4]; // rgba when material is omitted - mjString material; // name of material used for rendering + mjString* material; // name of material used for rendering // mesh properties - mjStringVec vertbody; // vertex body names - mjDoubleVec vert; // vertex positions - mjIntVec elem; // element vertex ids - mjFloatVec texcoord; // vertex texture coordinates + mjStringVec* vertbody; // vertex body names + mjDoubleVec* vert; // vertex positions + mjIntVec* elem; // element vertex ids + mjFloatVec* texcoord; // vertex texture coordinates // other - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsFlex; typedef struct mjsMesh_ { // mesh specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name - mjString content_type; // content type of file - mjString file; // mesh file + mjString* name; // name + mjString* classname; // class name + mjString* content_type; // content type of file + mjString* file; // mesh file double refpos[3]; // reference position double refquat[4]; // reference orientation double scale[3]; // rescale mesh mjtByte smoothnormal; // do not exclude large-angle faces from normals - mjFloatVec uservert; // user vertex data - mjFloatVec usernormal; // user normal data - mjFloatVec usertexcoord; // user texcoord data - mjIntVec userface; // user vertex indices - mjIntVec userfacenormal; // user normal indices - mjIntVec userfacetexcoord; // user texcoord indices + mjFloatVec* uservert; // user vertex data + mjFloatVec* usernormal; // user normal data + mjFloatVec* usertexcoord; // user texcoord data + mjIntVec* userface; // user vertex indices + mjIntVec* userfacenormal; // user normal indices + mjIntVec* userfacetexcoord; // user texcoord indices mjsPlugin plugin; // sdf plugin - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsMesh; typedef struct mjsHField_ { // height field specification mjElement* element; // element type - mjString name; // name - mjString content_type; // content type of file - mjString file; // file: (nrow, ncol, [elevation data]) + mjString* name; // name + mjString* content_type; // content type of file + mjString* file; // file: (nrow, ncol, [elevation data]) double size[4]; // hfield size (ignore referencing geom size) int nrow; // number of rows int ncol; // number of columns - mjFloatVec userdata; // user-provided elevation data - mjString info; // message appended to compiler errors + mjFloatVec* userdata; // user-provided elevation data + mjString* info; // message appended to compiler errors } mjsHField; typedef struct mjsSkin_ { // skin specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name - mjString file; // skin file - mjString material; // name of material used for rendering + mjString* name; // name + mjString* classname; // class name + mjString* file; // skin file + mjString* material; // name of material used for rendering float rgba[4]; // rgba when material is omitted float inflate; // inflate in normal direction int group; // group for visualization // mesh - mjFloatVec vert; // vertex positions - mjFloatVec texcoord; // texture coordinates - mjIntVec face; // faces + mjFloatVec* vert; // vertex positions + mjFloatVec* texcoord; // texture coordinates + mjIntVec* face; // faces // skin - mjStringVec bodyname; // body names - mjFloatVec bindpos; // bind pos - mjFloatVec bindquat; // bind quat - mjIntVecVec vertid; // vertex ids - mjFloatVecVec vertweight; // vertex weights + mjStringVec* bodyname; // body names + mjFloatVec* bindpos; // bind pos + mjFloatVec* bindquat; // bind quat + mjIntVecVec* vertid; // vertex ids + mjFloatVecVec* vertweight; // vertex weights // other - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsSkin; typedef struct mjsTexture_ { // texture specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name + mjString* name; // name + mjString* classname; // class name mjtTexture type; // texture type // method 1: builtin @@ -500,28 +515,28 @@ typedef struct mjsTexture_ { // texture specification int width; // width in pixels // method 2: single file - mjString content_type; // content type of file - mjString file; // png file to load; use for all sides of cube + mjString* content_type; // content type of file + mjString* file; // png file to load; use for all sides of cube int gridsize[2]; // size of grid for composite file; (1,1)-repeat char gridlayout[13]; // row-major: L,R,F,B,U,D for faces; . for unused // method 3: separate files - mjStringVec cubefiles; // different file for each side of the cube + mjStringVec* cubefiles; // different file for each side of the cube // flip options mjtByte hflip; // horizontal flip mjtByte vflip; // vertical flip // other - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsTexture; typedef struct mjsMaterial_ { // material specification mjElement* element; // element type - mjString name; // name - mjString classname; // class name - mjString texture; // name of texture (empty: none) + mjString* name; // name + mjString* classname; // class name + mjString* texture; // name of texture (empty: none) mjtByte texuniform; // make texture cube uniform float texrepeat[2]; // texture repetition for 2D mapping float emission; // emission @@ -531,16 +546,16 @@ typedef struct mjsMaterial_ { // material specification float metallic; // metallic float roughness; // roughness float rgba[4]; // rgba - mjString info; // message appended to compiler errors + mjString* info; // message appended to compiler errors } mjsMaterial; typedef struct mjsPair_ { mjElement* element; // element type - mjString name; // name - mjString classname; // class name - mjString geomname1; // name of geom 1 - mjString geomname2; // name of geom 2 + mjString* name; // name + mjString* classname; // class name + mjString* geomname1; // name of geom 1 + mjString* geomname2; // name of geom 2 // optional parameters: computed from geoms if not set by user int condim; // contact dimensionality @@ -550,38 +565,38 @@ typedef struct mjsPair_ { double margin; // margin for contact detection double gap; // include in solver if dist(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.file = (mjString)&spec_file_; - spec.content_type = (mjString)&spec_content_type_; - spec.uservert = (mjFloatVec)&spec_vert_; - spec.usernormal = (mjFloatVec)&spec_normal_; - spec.userface = (mjIntVec)&spec_face_; - spec.usertexcoord = (mjFloatVec)&spec_texcoord_; - spec.userfacetexcoord = (mjIntVec)&spec_facetexcoord_; - spec.plugin.name = (mjString)&plugin_name; - spec.plugin.instance_name = (mjString)&plugin_instance_name; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.file = &spec_file_; + spec.content_type = &spec_content_type_; + spec.uservert = &spec_vert_; + spec.usernormal = &spec_normal_; + spec.userface = &spec_face_; + spec.usertexcoord = &spec_texcoord_; + spec.userfacetexcoord = &spec_facetexcoord_; + spec.plugin.name = &plugin_name; + spec.plugin.instance_name = &plugin_instance_name; + spec.info = &info; } @@ -221,13 +221,13 @@ void mjCMesh::CopyFromSpec() { face_ = spec_face_; texcoord_ = spec_texcoord_; facetexcoord_ = spec_facetexcoord_; - file = (mjString)&file_; - content_type = (mjString)&content_type_; - uservert = (mjFloatVec)&vert_; - usernormal = (mjFloatVec)&normal_; - userface = (mjIntVec)&face_; - usertexcoord = (mjFloatVec)&texcoord_; - userfacetexcoord = (mjIntVec)&facetexcoord_; + file = &file_; + content_type = &content_type_; + uservert = &vert_; + usernormal = &normal_; + userface = &face_; + usertexcoord = &texcoord_; + userfacetexcoord = &facetexcoord_; plugin.active = spec.plugin.active; plugin.instance = spec.plugin.instance; plugin.name = spec.plugin.name; @@ -1979,19 +1979,19 @@ mjCSkin& mjCSkin::operator=(const mjCSkin& other) { void mjCSkin::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.file = (mjString)&spec_file_; - spec.material = (mjString)&spec_material_; - spec.vert = (mjFloatVec)&spec_vert_; - spec.texcoord = (mjFloatVec)&spec_texcoord_; - spec.face = (mjIntVec)&spec_face_; - spec.bodyname = (mjStringVec)&spec_bodyname_; - spec.bindpos = (mjFloatVec)&spec_bindpos_; - spec.bindquat = (mjFloatVec)&spec_bindquat_; - spec.vertid = (mjIntVecVec)&spec_vertid_; - spec.vertweight = (mjFloatVecVec)&spec_vertweight_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.file = &spec_file_; + spec.material = &spec_material_; + spec.vert = &spec_vert_; + spec.texcoord = &spec_texcoord_; + spec.face = &spec_face_; + spec.bodyname = &spec_bodyname_; + spec.bindpos = &spec_bindpos_; + spec.bindquat = &spec_bindquat_; + spec.vertid = &spec_vertid_; + spec.vertweight = &spec_vertweight_; + spec.info = &info; } @@ -2016,16 +2016,16 @@ void mjCSkin::CopyFromSpec() { bindquat_ = spec_bindquat_; vertid_ = spec_vertid_; vertweight_ = spec_vertweight_; - file = (mjString)&spec_file_; - material = (mjString)&spec_material_; - vert = (mjFloatVec)&spec_vert_; - texcoord = (mjFloatVec)&spec_texcoord_; - face = (mjIntVec)&spec_face_; - bodyname = (mjStringVec)&spec_bodyname_; - bindpos = (mjFloatVec)&spec_bindpos_; - bindquat = (mjFloatVec)&spec_bindquat_; - vertid = (mjIntVecVec)&spec_vertid_; - vertweight = (mjFloatVecVec)&spec_vertweight_; + file = &spec_file_; + material = &spec_material_; + vert = &spec_vert_; + texcoord = &spec_texcoord_; + face = &spec_face_; + bodyname = &spec_bodyname_; + bindpos = &spec_bindpos_; + bindquat = &spec_bindquat_; + vertid = &spec_vertid_; + vertweight = &spec_vertweight_; } @@ -2391,14 +2391,14 @@ mjCFlex& mjCFlex::operator=(const mjCFlex& other) { void mjCFlex::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.material = (mjString)&spec_material_; - spec.vertbody = (mjStringVec)&spec_vertbody_; - spec.vert = (mjDoubleVec)&spec_vert_; - spec.texcoord = (mjFloatVec)&spec_texcoord_; - spec.elem = (mjIntVec)&spec_elem_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.material = &spec_material_; + spec.vertbody = &spec_vertbody_; + spec.vert = &spec_vert_; + spec.texcoord = &spec_texcoord_; + spec.elem = &spec_elem_; + spec.info = &info; } @@ -2413,17 +2413,17 @@ void mjCFlex::NameSpace(const mjCModel* m) { void mjCFlex::CopyFromSpec() { *static_cast(this) = spec; - spec.info = (mjString)&info; + spec.info = &info; material_ = spec_material_; vertbody_ = spec_vertbody_; vert_ = spec_vert_; texcoord_ = spec_texcoord_; elem_ = spec_elem_; - material = (mjString)&material_; - vertbody = (mjStringVec)&vertbody_; - vert = (mjDoubleVec)&vert_; - texcoord = (mjFloatVec)&texcoord_; - elem = (mjIntVec)&elem_; + material = &material_; + vertbody = &vertbody_; + vert = &vert_; + texcoord = &texcoord_; + elem = &elem_; // clear precompiled asset. TODO: use asset cache nedge = 0; diff --git a/src/user/user_model.cc b/src/user/user_model.cc index f2bcbe5a..0a4f6f44 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -417,11 +417,11 @@ void mjCModel::CreateObjectLists() { void mjCModel::PointToLocal() { spec.element = static_cast(this); - spec.comment = (mjString)&spec_comment_; - spec.modelfiledir = (mjString)&spec_modelfiledir_; - spec.modelname = (mjString)&spec_modelname_; - spec.meshdir = (mjString)&spec_meshdir_; - spec.texturedir = (mjString)&spec_texturedir_; + spec.comment = &spec_comment_; + spec.modelfiledir = &spec_modelfiledir_; + spec.modelname = &spec_modelname_; + spec.meshdir = &spec_meshdir_; + spec.texturedir = &spec_texturedir_; } @@ -433,11 +433,11 @@ void mjCModel::CopyFromSpec() { modelname_ = spec_modelname_; meshdir_ = spec_meshdir_; texturedir_ = spec_texturedir_; - comment = (mjString)&comment_; - modelfiledir = (mjString)&modelfiledir_; - modelname = (mjString)&modelname_; - meshdir = (mjString)&meshdir_; - texturedir = (mjString)&texturedir_; + comment = &comment_; + modelfiledir = &modelfiledir_; + modelname = &modelname_; + meshdir = &meshdir_; + texturedir = &texturedir_; } diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index d59e4950..6ff8c13c 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -641,7 +641,7 @@ void mjCDef::PointToLocal() { tendon_.PointToLocal(); actuator_.PointToLocal(); spec.element = static_cast(this); - spec.name = (mjString)&name; + spec.name = &name; spec.joint = &joint_.spec; spec.geom = &geom_.spec; spec.site = &site_.spec; @@ -950,19 +950,19 @@ mjCBody& mjCBody::operator-=(const mjCBody& subtree) { void mjCBody::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.childclass = (mjString)&classname; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.plugin.name = (mjString)&plugin_name; - spec.plugin.instance_name = (mjString)&plugin_instance_name; - spec.info = (mjString)&info; + spec.name = &name; + spec.childclass = &classname; + spec.userdata = &spec_userdata_; + spec.plugin.name = &plugin_name; + spec.plugin.instance_name = (&plugin_instance_name); + spec.info = &info; } void mjCBody::CopyFromSpec() { *static_cast(this) = spec; userdata_ = spec_userdata_; - userdata = (mjDoubleVec)&userdata_; + userdata = &userdata_; plugin.active = spec.plugin.active; plugin.instance = spec.plugin.instance; plugin.name = spec.plugin.name; @@ -1670,9 +1670,9 @@ void mjCFrame::SetParent(mjCBody* _body) { void mjCFrame::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.childclass = (mjString)&classname; - spec.info = (mjString)&info; + spec.name = &name; + spec.childclass = &classname; + spec.info = &info; } @@ -1766,10 +1766,10 @@ bool mjCJoint::is_actfrclimited() const { return islimited(actfrclimited, actfrc void mjCJoint::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.userdata = &spec_userdata_; + spec.info = &info; } @@ -1777,7 +1777,7 @@ void mjCJoint::PointToLocal() { void mjCJoint::CopyFromSpec() { *static_cast(this) = spec; userdata_ = spec_userdata_; - userdata = (mjDoubleVec)&spec_userdata_; + userdata = &spec_userdata_; } @@ -1966,15 +1966,15 @@ mjCGeom& mjCGeom::operator=(const mjCGeom& other) { // to be called after any default copy constructor void mjCGeom::PointToLocal(void) { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.info = (mjString)&info; - spec.classname = (mjString)&classname; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.material = (mjString)&spec_material_; - spec.meshname = (mjString)&spec_meshname_; - spec.hfieldname = (mjString)&spec_hfieldname_; - spec.plugin.name = (mjString)&plugin_name; - spec.plugin.instance_name = (mjString)&plugin_instance_name; + spec.name = &name; + spec.info = &info; + spec.classname = &classname; + spec.userdata = &spec_userdata_; + spec.material = &spec_material_; + spec.meshname = &spec_meshname_; + spec.hfieldname = &spec_hfieldname_; + spec.plugin.name = &plugin_name; + spec.plugin.instance_name = &plugin_instance_name; } @@ -1985,10 +1985,10 @@ void mjCGeom::CopyFromSpec() { hfieldname_ = spec_hfieldname_; meshname_ = spec_meshname_; material_ = spec_material_; - userdata = (mjDoubleVec)&userdata_; - hfieldname = (mjString)&hfieldname_; - meshname = (mjString)&meshname_; - material = (mjString)&material_; + userdata = &userdata_; + hfieldname = &hfieldname_; + meshname = &meshname_; + material = &material_; plugin.active = spec.plugin.active; plugin.instance = spec.plugin.instance; plugin.name = spec.plugin.name; @@ -2599,11 +2599,11 @@ mjCSite& mjCSite::operator=(const mjCSite& other) { void mjCSite::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.info = (mjString)&info; - spec.classname = (mjString)&classname; - spec.material = (mjString)&spec_material_; - spec.userdata = (mjDoubleVec)&spec_userdata_; + spec.name = &name; + spec.info = &info; + spec.classname = &classname; + spec.material = &spec_material_; + spec.userdata = &spec_userdata_; } @@ -2612,8 +2612,8 @@ void mjCSite::CopyFromSpec() { *static_cast(this) = spec; userdata_ = spec_userdata_; material_ = spec_material_; - userdata = (mjDoubleVec)&userdata_; - material = (mjString)&material_; + userdata = &userdata_; + material = &material_; } @@ -2751,11 +2751,11 @@ mjCCamera& mjCCamera::operator=(const mjCCamera& other) { void mjCCamera::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.targetbody = (mjString)&spec_targetbody_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.userdata = &spec_userdata_; + spec.targetbody = &spec_targetbody_; + spec.info = &info; } @@ -2775,8 +2775,8 @@ void mjCCamera::CopyFromSpec() { *static_cast(this) = spec; userdata_ = spec_userdata_; targetbody_ = spec_targetbody_; - userdata = (mjDoubleVec)&userdata_; - targetbody = (mjString)&targetbody_; + userdata = &userdata_; + targetbody = &targetbody_; } @@ -2902,10 +2902,10 @@ mjCLight& mjCLight::operator=(const mjCLight& other) { void mjCLight::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.targetbody = (mjString)&spec_targetbody_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.targetbody = &spec_targetbody_; + spec.info = &info; } @@ -2924,7 +2924,7 @@ void mjCLight::NameSpace(const mjCModel* m) { void mjCLight::CopyFromSpec() { *static_cast(this) = spec; targetbody_ = spec_targetbody_; - targetbody = (mjString)&targetbody_; + targetbody = &targetbody_; } @@ -3002,11 +3002,11 @@ mjCHField& mjCHField::operator=(const mjCHField& other) { void mjCHField::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.file = (mjString)&spec_file_; - spec.content_type = (mjString)&spec_content_type_; - spec.userdata = (mjFloatVec)&spec_userdata_; - spec.info = (mjString)&info; + spec.name = &name; + spec.file = &spec_file_; + spec.content_type = &spec_content_type_; + spec.userdata = &spec_userdata_; + spec.info = &info; } @@ -3016,9 +3016,9 @@ void mjCHField::CopyFromSpec() { file_ = spec_file_; content_type_ = spec_content_type_; userdata_ = spec_userdata_; - file = (mjString)&file_; - content_type = (mjString)&content_type_; - userdata = (mjFloatVec)&userdata_; + file = &file_; + content_type = &content_type_; + userdata = &userdata_; // clear precompiled asset. TODO: use asset cache data.clear(); @@ -3231,12 +3231,12 @@ mjCTexture& mjCTexture::operator=(const mjCTexture& other) { void mjCTexture::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.file = (mjString)&spec_file_; - spec.content_type = (mjString)&spec_content_type_; - spec.cubefiles = (mjStringVec)&spec_cubefiles_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.file = &spec_file_; + spec.content_type = &spec_content_type_; + spec.cubefiles = &spec_cubefiles_; + spec.info = &info; } @@ -3246,9 +3246,9 @@ void mjCTexture::CopyFromSpec() { file_ = spec_file_; content_type_ = spec_content_type_; cubefiles_ = spec_cubefiles_; - file = (mjString)&file_; - content_type = (mjString)&content_type_; - cubefiles = (mjStringVec)&cubefiles_; + file = &file_; + content_type = &content_type_; + cubefiles = &cubefiles_; // clear precompiled asset. TODO: use asset cache rgb.clear(); @@ -3942,10 +3942,10 @@ mjCMaterial& mjCMaterial::operator=(const mjCMaterial& other) { void mjCMaterial::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.texture = (mjString)&spec_texture_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.texture = &spec_texture_; + spec.info = &info; } @@ -3953,7 +3953,7 @@ void mjCMaterial::PointToLocal() { void mjCMaterial::CopyFromSpec() { *static_cast(this) = spec; texture_ = spec_texture_; - texture = (mjString)&texture_; + texture = &texture_; } @@ -4032,11 +4032,11 @@ mjCPair& mjCPair::operator=(const mjCPair& other) { void mjCPair::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.geomname1 = (mjString)&spec_geomname1_; - spec.geomname2 = (mjString)&spec_geomname2_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.geomname1 = &spec_geomname1_; + spec.geomname2 = &spec_geomname2_; + spec.info = &info; } @@ -4055,8 +4055,8 @@ void mjCPair::CopyFromSpec() { *static_cast(this) = spec; geomname1_ = spec_geomname1_; geomname2_ = spec_geomname2_; - geomname1 = (mjString)&geomname1_; - geomname2 = (mjString)&geomname2_; + geomname1 = &geomname1_; + geomname2 = &geomname2_; } @@ -4257,10 +4257,10 @@ mjCBodyPair& mjCBodyPair::operator=(const mjCBodyPair& other) { void mjCBodyPair::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.bodyname1 = (mjString)&spec_bodyname1_; - spec.bodyname2 = (mjString)&spec_bodyname2_; - spec.info = (mjString)&info; + spec.name = &name; + spec.bodyname1 = &spec_bodyname1_; + spec.bodyname2 = &spec_bodyname2_; + spec.info = &info; } @@ -4279,8 +4279,8 @@ void mjCBodyPair::CopyFromSpec() { *static_cast(this) = spec; bodyname1_ = spec_bodyname1_; bodyname2_ = spec_bodyname2_; - bodyname1 = (mjString)&bodyname1_; - bodyname2 = (mjString)&bodyname2_; + bodyname1 = &bodyname1_; + bodyname2 = &bodyname2_; } @@ -4391,11 +4391,11 @@ mjCEquality& mjCEquality::operator=(const mjCEquality& other) { void mjCEquality::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.name1 = (mjString)&spec_name1_; - spec.name2 = (mjString)&spec_name2_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.name1 = &spec_name1_; + spec.name2 = &spec_name2_; + spec.info = &info; } @@ -4414,8 +4414,8 @@ void mjCEquality::CopyFromSpec() { *static_cast(this) = spec; name1_ = spec_name1_; name2_ = spec_name2_; - name1 = (mjString)&name1_; - name2 = (mjString)&name2_; + name1 = &name1_; + name2 = &name2_; } @@ -4560,11 +4560,11 @@ bool mjCTendon::is_limited() const { return islimited(limited, range); } void mjCTendon::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.material = (mjString)&spec_material_; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.material = &spec_material_; + spec.userdata = &spec_userdata_; + spec.info = &info; } @@ -4583,8 +4583,8 @@ void mjCTendon::CopyFromSpec() { *static_cast(this) = spec; material_ = spec_material_; userdata_ = spec_userdata_; - material = (mjString)&material_; - userdata = (mjDoubleVec)&userdata_; + material = &material_; + userdata = &userdata_; // clear precompiled for (int i=0; i(this); - spec.info = (mjString)&info; + spec.info = &info; } @@ -5041,15 +5041,15 @@ bool mjCActuator::is_actlimited() const { return islimited(actlimited, actrange) void mjCActuator::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.target = (mjString)&spec_target_; - spec.refsite = (mjString)&spec_refsite_; - spec.slidersite = (mjString)&spec_slidersite_; - spec.plugin.name = (mjString)&plugin_name; - spec.plugin.instance_name = (mjString)&plugin_instance_name; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.userdata = &spec_userdata_; + spec.target = &spec_target_; + spec.refsite = &spec_refsite_; + spec.slidersite = &spec_slidersite_; + spec.plugin.name = &plugin_name; + spec.plugin.instance_name = &plugin_instance_name; + spec.info = &info; } @@ -5071,10 +5071,10 @@ void mjCActuator::CopyFromSpec() { target_ = spec_target_; refsite_ = spec_refsite_; slidersite_ = spec_slidersite_; - userdata = (mjDoubleVec)&userdata_; - target = (mjString)&target_; - refsite = (mjString)&refsite_; - slidersite = (mjString)&slidersite_; + userdata = &userdata_; + target = &target_; + refsite = &refsite_; + slidersite = &slidersite_; plugin.active = spec.plugin.active; plugin.instance = spec.plugin.instance; plugin.name = spec.plugin.name; @@ -5364,14 +5364,14 @@ mjCSensor& mjCSensor::operator=(const mjCSensor& other) { void mjCSensor::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.classname = (mjString)&classname; - spec.userdata = (mjDoubleVec)&spec_userdata_; - spec.objname = (mjString)&spec_objname_; - spec.refname = (mjString)&spec_refname_; - spec.plugin.name = (mjString)&plugin_name; - spec.plugin.instance_name = (mjString)&plugin_instance_name; - spec.info = (mjString)&info; + spec.name = &name; + spec.classname = &classname; + spec.userdata = &spec_userdata_; + spec.objname = &spec_objname_; + spec.refname = &spec_refname_; + spec.plugin.name = &plugin_name; + spec.plugin.instance_name = &plugin_instance_name; + spec.info = &info; } @@ -5391,9 +5391,9 @@ void mjCSensor::CopyFromSpec() { userdata_ = spec_userdata_; objname_ = spec_objname_; refname_ = spec_refname_; - userdata = (mjDoubleVec)&userdata_; - objname = (mjString)&objname_; - refname = (mjString)&refname_; + userdata = &userdata_; + objname = &objname_; + refname = &refname_; plugin.active = spec.plugin.active; plugin.instance = spec.plugin.instance; plugin.name = spec.plugin.name; @@ -5867,9 +5867,9 @@ mjCNumeric& mjCNumeric::operator=(const mjCNumeric& other) { void mjCNumeric::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.data = (mjDoubleVec)&spec_data_; - spec.info = (mjString)&info; + spec.name = &name; + spec.data = &spec_data_; + spec.info = &info; } @@ -5877,7 +5877,7 @@ void mjCNumeric::PointToLocal() { void mjCNumeric::CopyFromSpec() { *static_cast(this) = spec; data_ = spec_data_; - data = (mjDoubleVec)&data_; + data = &data_; } @@ -5956,9 +5956,9 @@ mjCText& mjCText::operator=(const mjCText& other) { void mjCText::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.data = (mjString)&spec_data_; - spec.info = (mjString)&info; + spec.name = &name; + spec.data = &spec_data_; + spec.info = &info; } @@ -5966,7 +5966,7 @@ void mjCText::PointToLocal() { void mjCText::CopyFromSpec() { *static_cast(this) = spec; data_ = spec_data_; - data = (mjString)&data_; + data = &data_; } @@ -6036,11 +6036,11 @@ mjCTuple& mjCTuple::operator=(const mjCTuple& other) { void mjCTuple::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.objtype = (mjIntVec)&spec_objtype_; - spec.objname = (mjStringVec)&spec_objname_; - spec.objprm = (mjDoubleVec)&spec_objprm_; - spec.info = (mjString)&info; + spec.name = &name; + spec.objtype = (mjIntVec*)&spec_objtype_; + spec.objname = &spec_objname_; + spec.objprm = &spec_objprm_; + spec.info = &info; } @@ -6061,9 +6061,9 @@ void mjCTuple::CopyFromSpec() { objtype_ = spec_objtype_; objname_ = spec_objname_; objprm_ = spec_objprm_; - objtype = (mjIntVec)&objtype_; - objname = (mjStringVec)&objname_; - objprm = (mjDoubleVec)&objprm_; + objtype = (mjIntVec*)&objtype_; + objname = &objname_; + objprm = &objprm_; } @@ -6171,14 +6171,14 @@ mjCKey& mjCKey::operator=(const mjCKey& other) { void mjCKey::PointToLocal() { spec.element = static_cast(this); - spec.name = (mjString)&name; - spec.qpos = (mjDoubleVec)&spec_qpos_; - spec.qvel = (mjDoubleVec)&spec_qvel_; - spec.act = (mjDoubleVec)&spec_act_; - spec.mpos = (mjDoubleVec)&spec_mpos_; - spec.mquat = (mjDoubleVec)&spec_mquat_; - spec.ctrl = (mjDoubleVec)&spec_ctrl_; - spec.info = (mjString)&info; + spec.name = &name; + spec.qpos = &spec_qpos_; + spec.qvel = &spec_qvel_; + spec.act = &spec_act_; + spec.mpos = &spec_mpos_; + spec.mquat = &spec_mquat_; + spec.ctrl = &spec_ctrl_; + spec.info = &info; } @@ -6191,12 +6191,12 @@ void mjCKey::CopyFromSpec() { mpos_ = spec_mpos_; mquat_ = spec_mquat_; ctrl_ = spec_ctrl_; - qpos = (mjDoubleVec)&qpos_; - qvel = (mjDoubleVec)&qvel_; - act = (mjDoubleVec)&act_; - mpos = (mjDoubleVec)&mpos_; - mquat = (mjDoubleVec)&mquat_; - ctrl = (mjDoubleVec)&ctrl_; + qpos = &qpos_; + qvel = &qvel_; + act = &act_; + mpos = &mpos_; + mquat = &mquat_; + ctrl = &ctrl_; } @@ -6314,9 +6314,9 @@ mjCPlugin::mjCPlugin(mjCModel* _model) { // public interface mjs_defaultPlugin(&spec); elemtype = mjOBJ_PLUGIN; - spec.name = (mjString)&name; - spec.instance_name = (mjString)&instance_name; - spec.info = (mjString)&info; + spec.name = &name; + spec.instance_name = &instance_name; + spec.info = &info; } From fb8bf206d960583a6f9b9e752ca429d15991bce4 Mon Sep 17 00:00:00 2001 From: Baruch Tabanpour Date: Thu, 6 Jun 2024 16:42:33 -0700 Subject: [PATCH 13/32] Update MJX colab with hfield example. PiperOrigin-RevId: 641061897 Change-Id: Ia1002173adce7b7b12203857abed18568ae236f1 --- mjx/tutorial.ipynb | 198 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 174 insertions(+), 24 deletions(-) diff --git a/mjx/tutorial.ipynb b/mjx/tutorial.ipynb index 7855fa9d..7f13bb37 100644 --- a/mjx/tutorial.ipynb +++ b/mjx/tutorial.ipynb @@ -62,7 +62,7 @@ "source": [ "!pip install mujoco\n", "!pip install mujoco_mjx\n", - "!pip install brax" + "!pip install brax\n" ] }, { @@ -102,11 +102,6 @@ "}\n", "\"\"\")\n", "\n", - "# Tell XLA to use Triton GEMM, this improves steps/sec by ~30% on some GPUs\n", - "xla_flags = os.environ.get('XLA_FLAGS', '')\n", - "xla_flags += ' --xla_gpu_triton_gemm_any=True'\n", - "os.environ['XLA_FLAGS'] = xla_flags\n", - "\n", "# Configure MuJoCo to use the EGL rendering backend (requires GPU)\n", "print('Setting environment variable to use GPU rendering:')\n", "%env MUJOCO_GL=egl\n", @@ -122,7 +117,12 @@ " 'If using a hosted Colab runtime, make sure you enable GPU acceleration '\n", " 'by going to the Runtime menu and selecting \"Choose runtime type\".')\n", "\n", - "print('Installation successful.')" + "print('Installation successful.')\n", + "\n", + "# Tell XLA to use Triton GEMM, this improves steps/sec by ~30% on some GPUs\n", + "xla_flags = os.environ.get('XLA_FLAGS', '')\n", + "xla_flags += ' --xla_gpu_triton_gemm_any=True'\n", + "os.environ['XLA_FLAGS'] = xla_flags\n" ] }, { @@ -155,20 +155,32 @@ "cell_type": "code", "execution_count": 0, "metadata": { + "cellView": "form", "id": "ObF1UXrkb0Nd" }, "outputs": [], "source": [ "#@title Import MuJoCo, MJX, and Brax\n", - "\n", - "\n", "from datetime import datetime\n", + "from etils import epath\n", "import functools\n", "from IPython.display import HTML\n", + "from typing import Any, Dict, Sequence, Tuple, Union\n", + "import os\n", + "from ml_collections import config_dict\n", + "\n", + "\n", "import jax\n", "from jax import numpy as jp\n", "import numpy as np\n", - "from typing import Any, Dict, Sequence, Tuple, Union\n", + "from flax.training import orbax_utils\n", + "from flax import struct\n", + "from matplotlib import pyplot as plt\n", + "import mediapy as media\n", + "from orbax import checkpoint as ocp\n", + "\n", + "import mujoco\n", + "from mujoco import mjx\n", "\n", "from brax import base\n", "from brax import envs\n", @@ -178,15 +190,7 @@ "from brax.mjx.base import State as MjxState\n", "from brax.training.agents.ppo import train as ppo\n", "from brax.training.agents.ppo import networks as ppo_networks\n", - "from brax.io import html, mjcf, model\n", - "\n", - "from etils import epath\n", - "from flax import struct\n", - "from matplotlib import pyplot as plt\n", - "import mediapy as media\n", - "from ml_collections import config_dict\n", - "import mujoco\n", - "from mujoco import mjx\n" + "from brax.io import html, mjcf, model\n" ] }, { @@ -892,7 +896,7 @@ }, "outputs": [], "source": [ - "!git clone https://github.com/google-deepmind/mujoco_menagerie" + "!git clone https://github.com/google-deepmind/mujoco_menagerie\n" ] }, { @@ -972,9 +976,11 @@ " obs_noise: float = 0.05,\n", " action_scale: float = 0.3,\n", " kick_vel: float = 0.05,\n", + " scene_file: str = 'scene_mjx.xml',\n", " **kwargs,\n", " ):\n", - " path = epath.Path('mujoco_menagerie/google_barkour_vb/scene_mjx.xml')\n", + "# path = epath.Path('mujoco_menagerie/google_barkour_vb')\n", + " path = path / scene_file\n", " sys = mjcf.load(path.as_posix())\n", " self._dt = 0.02 # this environment is 50 fps\n", " sys = sys.tree_replace({'opt.timestep': 0.004})\n", @@ -1284,10 +1290,11 @@ " return done & (step < 500)\n", "\n", " def render(\n", - " self, trajectory: List[base.State], camera: str | None = None\n", + " self, trajectory: List[base.State], camera: str | None = None,\n", + " width: int = 240, height: int = 320,\n", " ) -> Sequence[np.ndarray]:\n", " camera = camera or 'track'\n", - " return super().render(trajectory, camera=camera)\n", + " return super().render(trajectory, camera=camera, width=width, height=height)\n", "\n", "envs.register_environment('barkour', BarkourEnv)" ] @@ -1323,6 +1330,17 @@ }, "outputs": [], "source": [ + "ckpt_path = epath.Path('/tmp/quadrupred_joystick/ckpts')\n", + "ckpt_path.mkdir(parents=True, exist_ok=True)\n", + "\n", + "def policy_params_fn(current_step, make_policy, params):\n", + " # save checkpoints\n", + " orbax_checkpointer = ocp.PyTreeCheckpointer()\n", + " save_args = orbax_utils.save_args_from_target(params)\n", + " path = ckpt_path / f'{current_step}'\n", + " orbax_checkpointer.save(path, params, force=True, save_args=save_args)\n", + "\n", + "\n", "make_networks_factory = functools.partial(\n", " ppo_networks.make_ppo_networks,\n", " policy_hidden_layer_sizes=(128, 128, 128, 128))\n", @@ -1333,7 +1351,9 @@ " num_updates_per_batch=4, discounting=0.97, learning_rate=3.0e-4,\n", " entropy_cost=1e-2, num_envs=8192, batch_size=256,\n", " network_factory=make_networks_factory,\n", - " randomization_fn=domain_randomize, seed=0)\n", + " randomization_fn=domain_randomize,\n", + " policy_params_fn=policy_params_fn,\n", + " seed=0)\n", "\n", "x_data = []\n", "y_data = []\n", @@ -1450,6 +1470,136 @@ "source": [ "HTML(html.render(eval_env.sys.tree_replace({'opt.timestep': eval_env.dt}), rollout))" ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gNagGnBODotY" + }, + "source": [ + "## Train Policy with Height Field\n", + "\n", + "We may also want the quadruped to learn to walk on rought terrain. Let's take the latest checkpoint from the joystick policy above, and finetune it on a height field terrain." + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "id": "wlT3xLouKxqT" + }, + "outputs": [], + "source": [ + "# use the height field scene\n", + "scene_file = 'scene_hfield_mjx.xml'\n", + "\n", + "env = envs.get_environment(env_name, scene_file=scene_file)\n", + "jit_reset = jax.jit(env.reset)\n", + "state = jit_reset(jax.random.PRNGKey(0))\n", + "plt.imshow(env.render([state.pipeline_state], camera='track')[0])" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "id": "jOTx-OyPDqPW" + }, + "outputs": [], + "source": [ + "# grab the latest checkpoint from the flat terrain joystick policy\n", + "latest_ckpts = list(ckpt_path.glob('*'))\n", + "latest_ckpts.sort()\n", + "latest_ckpt = latest_ckpts[0]\n", + "\n", + "train_fn = functools.partial(\n", + " ppo.train, num_timesteps=40_000_000, num_evals=5,\n", + " reward_scaling=1, episode_length=1000, normalize_observations=True,\n", + " action_repeat=1, unroll_length=20, num_minibatches=32,\n", + " num_updates_per_batch=4, discounting=0.97, learning_rate=3.0e-4,\n", + " entropy_cost=1e-2, num_envs=8192, batch_size=256,\n", + " network_factory=make_networks_factory,\n", + " randomization_fn=domain_randomize, seed=0,\n", + " restore_checkpoint_path=latest_ckpt)\n", + "\n", + "x_data = []\n", + "y_data = []\n", + "ydataerr = []\n", + "times = [datetime.now()]\n", + "max_y, min_y = 40, 0\n", + "\n", + "# Reset environments since internals may be overwritten by tracers from the\n", + "# domain randomization function.\n", + "env = envs.get_environment(env_name, scene_file=scene_file)\n", + "eval_env = envs.get_environment(env_name, scene_file=scene_file)\n", + "make_inference_fn, params, _= train_fn(environment=env,\n", + " progress_fn=progress,\n", + " eval_env=eval_env)\n", + "\n", + "print(f'time to jit: {times[1] - times[0]}')\n", + "print(f'time to train: {times[-1] - times[1]}')" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "0wHRJyjLFww6" + }, + "source": [ + "## Visualize Policy with Height Field" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "id": "1X57XkaVFu-v" + }, + "outputs": [], + "source": [ + "eval_env = envs.get_environment(env_name, scene_file=scene_file)\n", + "\n", + "jit_reset = jax.jit(eval_env.reset)\n", + "jit_step = jax.jit(eval_env.step)\n", + "inference_fn = make_inference_fn(params)\n", + "jit_inference_fn = jax.jit(inference_fn)" + ] + }, + { + "cell_type": "code", + "execution_count": 0, + "metadata": { + "id": "nAxexZcVFu-v" + }, + "outputs": [], + "source": [ + "# @markdown Commands **only used for Barkour Env**:\n", + "x_vel = 1.0 #@param {type: \"number\"}\n", + "y_vel = 0.0 #@param {type: \"number\"}\n", + "ang_vel = -0.5 #@param {type: \"number\"}\n", + "\n", + "the_command = jp.array([x_vel, y_vel, ang_vel])\n", + "\n", + "# initialize the state\n", + "rng = jax.random.PRNGKey(0)\n", + "state = jit_reset(rng)\n", + "state.info['command'] = the_command\n", + "rollout = [state.pipeline_state]\n", + "\n", + "# grab a trajectory\n", + "n_steps = 500\n", + "render_every = 2\n", + "\n", + "for i in range(n_steps):\n", + " act_rng, rng = jax.random.split(rng)\n", + " ctrl, _ = jit_inference_fn(state.obs, act_rng)\n", + " state = jit_step(state, ctrl)\n", + " rollout.append(state.pipeline_state)\n", + "\n", + "media.show_video(\n", + " eval_env.render(rollout[::render_every], camera='track'),\n", + " fps=1.0 / eval_env.dt / render_every)" + ] } ], "metadata": { From ace0c8f0a33d10bbd3a5cbda4948382dce4459dd Mon Sep 17 00:00:00 2001 From: Baruch Tabanpour Date: Thu, 6 Jun 2024 19:04:21 -0700 Subject: [PATCH 14/32] Add maxhullvert. PiperOrigin-RevId: 641092710 Change-Id: Ib4fa828a9c82531614f0a67de9990340dbb25406 --- doc/XMLreference.rst | 9 ++++++++ doc/XMLschema.rst | 4 +++- doc/changelog.rst | 9 ++++++++ src/user/user_api.h | 1 + src/user/user_init.c | 1 + src/user/user_mesh.cc | 17 +++++++++----- src/user/user_objects.h | 1 + src/xml/xml_native_reader.cc | 12 +++++++--- test/user/testdata/torus_maxhullvert.xml | 8 +++++++ .../testdata/torus_maxhullvert_default.xml | 11 +++++++++ test/user/user_mesh_test.cc | 23 +++++++++++++++++++ 11 files changed, 86 insertions(+), 10 deletions(-) create mode 100644 test/user/testdata/torus_maxhullvert.xml create mode 100644 test/user/testdata/torus_maxhullvert_default.xml diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index 0b01759a..db20485c 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -1233,6 +1233,13 @@ The full list of processing steps applied by the compiler to each mesh is as fol faces at large angles relative to the average normal are excluded from the average. In this way, sharp edges (as in cube edges) are not smoothed. +.. _asset-mesh-maxhullvert: + +:at:`maxhullvert`: :at-val:`int, "-1"` + Maximum number of vertices in a mesh's convex hull. Currently this is implemented by asking qhull + `to teminate `__ after :at:`maxhullvert` vertices. The default + value of -1 means "unlimited". Positive values must be larger than 3. + .. _asset-mesh-vertex: :at:`vertex`: :at-val:`real(3*nvert), optional` @@ -7666,6 +7673,8 @@ if omitted. .. _default-mesh-scale: +.. _default-mesh-maxhullvert: + :el-prefix:`default/` |-| **mesh** (?) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst index 42a4131a..044d022b 100644 --- a/doc/XMLschema.rst +++ b/doc/XMLschema.rst @@ -103,6 +103,8 @@ | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | | | | | :ref:`refpos` | :ref:`refquat` | :ref:`scale` | :ref:`smoothnormal` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +| | | | :ref:`maxhullvert` | | | | | +| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_2| mesh |br| |_2| |L| | | .. table:: | | :ref:`plugin | \* | :class: mjcf-attributes | @@ -1325,7 +1327,7 @@ | :ref:`mesh | ? | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`scale` | | | | | +| | | | :ref:`scale` | :ref:`maxhullvert` | | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| default |br| |_| |L| | | .. table:: | diff --git a/doc/changelog.rst b/doc/changelog.rst index d1e6e023..5fe96611 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -2,6 +2,15 @@ Changelog ========= +Upcoming version (not yet released) +----------------------------------- + +General +^^^^^^^ + +1. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. + + Version 3.1.6 (Jun 3, 2024) --------------------------- diff --git a/src/user/user_api.h b/src/user/user_api.h index 96650d7e..1908791f 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -446,6 +446,7 @@ typedef struct mjsMesh_ { // mesh specification double refquat[4]; // reference orientation double scale[3]; // rescale mesh mjtByte smoothnormal; // do not exclude large-angle faces from normals + int maxhullvert; // maximum vertex count for the convex hull mjFloatVec* uservert; // user vertex data mjFloatVec* usernormal; // user normal data mjFloatVec* usertexcoord; // user texcoord data diff --git a/src/user/user_init.c b/src/user/user_init.c index b45c2dde..6a6c8d15 100644 --- a/src/user/user_init.c +++ b/src/user/user_init.c @@ -243,6 +243,7 @@ void mjs_defaultMesh(mjsMesh* mesh) { memset(mesh, 0, sizeof(mjsMesh)); mesh->refquat[0] = 1; mesh->scale[0] = mesh->scale[1] = mesh->scale[2] = 1; + mesh->maxhullvert = -1; } diff --git a/src/user/user_mesh.cc b/src/user/user_mesh.cc index 06860079..79a1d1d0 100644 --- a/src/user/user_mesh.cc +++ b/src/user/user_mesh.cc @@ -50,8 +50,8 @@ #include #include -#include #include +#include #include "engine/engine_crossplatform.h" #include "engine/engine_io.h" #include "engine/engine_plugin.h" @@ -135,6 +135,7 @@ mjCMesh::mjCMesh(mjCModel* _model, mjCDef* _def) { center_ = NULL; graph_ = NULL; needhull_ = false; + maxhullvert_ = -1; invalidorientation_.first = -1; invalidorientation_.second = -1; validarea_ = true; @@ -223,6 +224,7 @@ void mjCMesh::CopyFromSpec() { facetexcoord_ = spec_facetexcoord_; file = &file_; content_type = &content_type_; + maxhullvert_ = spec.maxhullvert; uservert = &vert_; usernormal = &normal_; userface = &face_; @@ -1548,12 +1550,17 @@ double& mjCMesh::GetVolumeRef(mjtGeomInertia type) { // make graph describing convex hull -void mjCMesh::MakeGraph(void) { +void mjCMesh::MakeGraph() { int adr, ok, curlong, totlong, exitcode; double* data; facetT* facet, **facetp; vertexT* vertex, *vertex1, **vertex1p; - char qhopt[10] = "qhull Qt"; + + std::string qhopt = "qhull Qt"; + if (maxhullvert_ > -1) { + // qhull "TA" actually means "number of vertices added after the initial simplex" + qhopt += " TA" + std::to_string(maxhullvert_ - 4); + } // graph not needed for small meshes if (nvert() < 4) { @@ -1585,7 +1592,7 @@ void mjCMesh::MakeGraph(void) { qh->NOerrexit = false; if (!exitcode) { // actual init - qh_initflags(qh, qhopt); + qh_initflags(qh, const_cast(qhopt.c_str())); qh_init_B(qh, data, nvert(), 3, False); // construct convex hull @@ -1742,8 +1749,6 @@ void mjCMesh::MakeGraph(void) { } } - - // copy graph into face data void mjCMesh::CopyGraph(void) { // only if face data is missing diff --git a/src/user/user_objects.h b/src/user/user_objects.h index 8860a6c9..e3548686 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -778,6 +778,7 @@ class mjCMesh_ : public mjCBase { // size of mesh data to be copied into mjModel int szgraph_; // size of graph data in ints bool needhull_; // needs convex hull for collisions + int maxhullvert_; // max vertex count of convex hull mjCBoundingVolumeHierarchy tree_; // bounding volume hierarchy std::vector face_aabb_; // bounding boxes of all faces diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 72a398b9..786e54ae 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -145,7 +145,7 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { {"default", "R", "1", "class"}, {"<"}, - {"mesh", "?", "1", "scale"}, + {"mesh", "?", "2", "scale", "maxhullvert"}, {"material", "?", "10", "texture", "emission", "specular", "shininess", "reflectance", "metallic", "roughness", "rgba", "texrepeat", "texuniform"}, {"joint", "?", "22", "type", "group", "pos", "axis", "springdamper", @@ -221,8 +221,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { {"asset", "*", "0"}, {"<"}, - {"mesh", "*", "12", "name", "class", "content_type", "file", "vertex", "normal", - "texcoord", "face", "refpos", "refquat", "scale", "smoothnormal"}, + {"mesh", "*", "13", "name", "class", "content_type", "file", "vertex", "normal", + "texcoord", "face", "refpos", "refquat", "scale", "smoothnormal", + "maxhullvert"}, {"<"}, {"plugin", "*", "2", "plugin", "instance"}, {"<"}, @@ -1391,6 +1392,11 @@ void mjXReader::OneMesh(XMLElement* elem, mjsMesh* pmesh) { pmesh->smoothnormal = (n==1); } + if (ReadAttrInt(elem, "maxhullvert", &n)) { + if (n != 0 && n < 4) throw mjXError(elem, "maxhullvert must be larger than 3"); + pmesh->maxhullvert = n; + } + // read user vertex data if (ReadAttrTxt(elem, "vertex", text)) { auto uservert = ReadAttrVec(elem, "vertex"); diff --git a/test/user/testdata/torus_maxhullvert.xml b/test/user/testdata/torus_maxhullvert.xml new file mode 100644 index 00000000..e9fd03fa --- /dev/null +++ b/test/user/testdata/torus_maxhullvert.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/user/testdata/torus_maxhullvert_default.xml b/test/user/testdata/torus_maxhullvert_default.xml new file mode 100644 index 00000000..72444264 --- /dev/null +++ b/test/user/testdata/torus_maxhullvert_default.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/test/user/user_mesh_test.cc b/test/user/user_mesh_test.cc index 56898655..cf62423d 100644 --- a/test/user/user_mesh_test.cc +++ b/test/user/user_mesh_test.cc @@ -41,6 +41,10 @@ static const char* const kCubePath = "user/testdata/cube.xml"; static const char* const kTorusPath = "user/testdata/torus.xml"; +static const char* const kTorusMaxhullVertPath = + "user/testdata/torus_maxhullvert.xml"; +static const char* const kTorusDefaultMaxhullVertPath = + "user/testdata/torus_maxhullvert_default.xml"; static const char* const kTorusShellPath = "user/testdata/torus_shell.xml"; static const char* const kConvexInertiaPath = @@ -405,6 +409,25 @@ TEST_F(MjCMeshTest, TinyMeshLoads) { mj_deleteModel(model); } +// ------------- test max hull vert ------------------------------------------- +TEST_F(MjCMeshTest, MaxHullVert) { + const std::string xml_path = GetTestDataFilePath(kTorusMaxhullVertPath); + std::array error; + mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size()); + ASSERT_GT(model->ngeom, 0); + ASSERT_EQ(model->mesh_graph[0], 4); + mj_deleteModel(model); +} + +TEST_F(MjCMeshTest, MaxHullVertDefault) { + const std::string xml_path = GetTestDataFilePath(kTorusDefaultMaxhullVertPath); + std::array error; + mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size()); + ASSERT_GT(model->ngeom, 0); + ASSERT_EQ(model->mesh_graph[0], 64); + mj_deleteModel(model); +} + // ------------- test inline loading ------------------------------------------ TEST_F(MjCMeshTest, FaceNormalAutogenerated) { static constexpr char xml[] = R"( From c136f3d6e8010d12cd276f14a468017012d9f8c2 Mon Sep 17 00:00:00 2001 From: Baruch Tabanpour Date: Thu, 6 Jun 2024 19:08:52 -0700 Subject: [PATCH 15/32] Fix barkour path. PiperOrigin-RevId: 641093437 Change-Id: I92b8193c8458461e1a1f80b1e4c099067ed2099a --- mjx/tutorial.ipynb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mjx/tutorial.ipynb b/mjx/tutorial.ipynb index 7f13bb37..024a9c8f 100644 --- a/mjx/tutorial.ipynb +++ b/mjx/tutorial.ipynb @@ -909,6 +909,9 @@ "source": [ "#@title Barkour vb Quadruped Env\n", "\n", + "BARKOUR_ROOT_PATH = epath.Path('mujoco_menagerie/google_barkour_vb')\n", + "\n", + "\n", "def get_config():\n", " \"\"\"Returns reward config for barkour quadruped environment.\"\"\"\n", "\n", @@ -979,8 +982,7 @@ " scene_file: str = 'scene_mjx.xml',\n", " **kwargs,\n", " ):\n", - "# path = epath.Path('mujoco_menagerie/google_barkour_vb')\n", - " path = path / scene_file\n", + " path = BARKOUR_ROOT_PATH / scene_file\n", " sys = mjcf.load(path.as_posix())\n", " self._dt = 0.02 # this environment is 50 fps\n", " sys = sys.tree_replace({'opt.timestep': 0.004})\n", From 5b6800116ab98429217b2ed22ed74bc76a880f73 Mon Sep 17 00:00:00 2001 From: Saran Tunyasuvunakool Date: Fri, 7 Jun 2024 07:57:34 -0700 Subject: [PATCH 16/32] Workaround for https://github.com/actions/runner-images/issues/10004. PiperOrigin-RevId: 641250258 Change-Id: I00509dc21c568a05d6cba346437194a7e80badd4 --- .github/workflows/build.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 76e861e6..874039dd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -154,6 +154,16 @@ jobs: - name: Build MuJoCo working-directory: build run: cmake --build . --config=Release ${{ matrix.cmake_build_args }} + - name: Copy in the correct VC runtime DLLs (workaround for actions/runner-images#10004) + if: ${{ runner.os == 'Windows' }} + working-directory: build + shell: powershell + run: | + Copy-Item (Join-Path ` + ((Get-ChildItem -Directory ` + -Path "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Redist\MSVC\14.*" | + Sort -Descending | Select-Object -First 1).FullName + ) 'x64\Microsoft.VC143.CRT\*.dll') "bin\Release" - name: Test MuJoCo working-directory: build run: ctest -C Release --output-on-failure . From 4c3d9461ae1fd0ea128a853ad8067cc313594903 Mon Sep 17 00:00:00 2001 From: Erik Frey Date: Fri, 7 Jun 2024 16:04:54 -0700 Subject: [PATCH 17/32] Elliptic friction in MJX. PiperOrigin-RevId: 641384367 Change-Id: I510c565940324fbbf392ce537ce27e0cb9af3eb1 --- doc/changelog.rst | 7 + doc/mjx.rst | 8 +- mjx/mujoco/mjx/_src/collision_convex.py | 5 +- mjx/mujoco/mjx/_src/constraint.py | 427 +++++++++++++---------- mjx/mujoco/mjx/_src/constraint_test.py | 50 ++- mjx/mujoco/mjx/_src/io.py | 36 +- mjx/mujoco/mjx/_src/io_test.py | 10 +- mjx/mujoco/mjx/_src/solver.py | 254 +++++++++++--- mjx/mujoco/mjx/_src/solver_test.py | 101 +++--- mjx/mujoco/mjx/_src/test_util.py | 23 ++ mjx/mujoco/mjx/_src/types.py | 7 +- mjx/mujoco/mjx/test_data/constraints.xml | 11 +- 12 files changed, 610 insertions(+), 329 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index 5fe96611..2e42197a 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -10,6 +10,13 @@ General 1. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. +MJX +~~~ + +2. Added support for :ref:`elliptic friction cones`. +3. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. +4. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. + Version 3.1.6 (Jun 3, 2024) --------------------------- diff --git a/doc/mjx.rst b/doc/mjx.rst index 361c9cc4..ec75e57c 100644 --- a/doc/mjx.rst +++ b/doc/mjx.rst @@ -198,13 +198,13 @@ The following features are **fully supported** in MJX: * - :ref:`Geom ` - ``PLANE``, ``HFIELD``, ``SPHERE``, ``CAPSULE``, ``BOX``, ``MESH`` are fully implemented. ``ELLIPSOID`` and ``CYLINDER`` are implemented but only collide with other primitives. * - :ref:`Constraint ` - - ``EQUALITY``, ``LIMIT_JOINT``, ``CONTACT_FRICTIONLESS``, ``CONTACT_PYRAMIDAL`` + - ``EQUALITY``, ``LIMIT_JOINT``, ``CONTACT_FRICTIONLESS``, ``CONTACT_PYRAMIDAL``, ``CONTACT_ELLIPTIC`` * - :ref:`Equality ` - ``CONNECT``, ``WELD``, ``JOINT`` * - :ref:`Integrator ` - ``EULER``, ``RK4`` * - :ref:`Cone ` - - ``PYRAMIDAL`` + - ``PYRAMIDAL``, ``ELLIPTIC`` * - :ref:`Condim ` - 1, 3, 4, 6 * - :ref:`Solver ` @@ -225,7 +225,7 @@ The following features are **in development** and coming soon: * - :ref:`Geom ` - ``SDF``. Collisions between (``SPHERE``, ``BOX``, ``MESH``, ``HFIELD``) and ``CYLINDER``. Collisions between (``BOX``, ``MESH``, ``HFIELD``) and ``ELLIPSOID``. * - :ref:`Constraint ` - - :ref:`Frictionloss `, ``CONTACT_ELLIPTIC``, ``FRICTION_DOF`` + - :ref:`Frictionloss `, ``FRICTION_DOF`` * - :ref:`Integrator ` - ``IMPLICIT``, ``IMPLICITFAST`` * - Dynamics @@ -240,8 +240,6 @@ The following features are **in development** and coming soon: - ``MUSCLE`` * - :ref:`Tendon Wrapping ` - ``NONE``, ``JOINT``, ``PULLEY``, ``SITE``, ``SPHERE``, ``CYLINDER`` - * - :ref:`Cone ` - - ``ELLIPTIC`` * - Fluid Model - :ref:`flEllipsoid` * - :ref:`Tendons ` diff --git a/mjx/mujoco/mjx/_src/collision_convex.py b/mjx/mujoco/mjx/_src/collision_convex.py index 7d2c10dd..55d96ef4 100644 --- a/mjx/mujoco/mjx/_src/collision_convex.py +++ b/mjx/mujoco/mjx/_src/collision_convex.py @@ -239,7 +239,8 @@ def plane_convex(plane: GeomInfo, convex: ConvexInfo) -> Collision: plane_pos = convex.mat.T @ (plane.pos - convex.pos) n = convex.mat.T @ plane.mat[:, 2] support = (plane_pos - vert) @ n - idx = _manifold_points(vert, support > jp.maximum(0, support.max() - 1e-4), n) + # search for manifold points within a 1mm skin depth + idx = _manifold_points(vert, support > jp.maximum(0, support.max() - 1e-3), n) pos = vert[idx] # convert to world frame @@ -970,6 +971,7 @@ def _box_box(b1: ConvexInfo, b2: ConvexInfo) -> Collision: # Go back to world frame. pos = b2.pos + pos @ b2.mat.T n = normal @ b2.mat.T + dist = jp.where(jp.isinf(dist), jp.finfo(float).max, dist) return dist, pos, n @@ -1029,6 +1031,7 @@ def _convex_convex(c1: ConvexInfo, c2: ConvexInfo) -> Collision: pos = c2.pos + pos @ c2.mat.T n = normal @ c2.mat.T n = -n if swapped else n + dist = jp.where(jp.isinf(dist), jp.finfo(float).max, dist) return dist, pos, n diff --git a/mjx/mujoco/mjx/_src/constraint.py b/mjx/mujoco/mjx/_src/constraint.py index b1683fe7..5a1286d1 100644 --- a/mjx/mujoco/mjx/_src/constraint.py +++ b/mjx/mujoco/mjx/_src/constraint.py @@ -24,6 +24,7 @@ from mujoco.mjx._src import math from mujoco.mjx._src import support # pylint: disable=g-importing-member from mujoco.mjx._src.dataclasses import PyTreeNode +from mujoco.mjx._src.types import ConeType from mujoco.mjx._src.types import ConstraintType from mujoco.mjx._src.types import Contact from mujoco.mjx._src.types import Data @@ -35,18 +36,14 @@ from mujoco.mjx._src.types import Model import numpy as np -_CONDIM_EFC_COUNT = {1: 1, 3: 4, 4: 6, 6: 10} - - class _Efc(PyTreeNode): """Support data for creating constraint matrices.""" J: jax.Array - pos: jax.Array - pos_norm: jax.Array + pos_aref: jax.Array + pos_imp: jax.Array invweight: jax.Array solref: jax.Array solimp: jax.Array - frictionloss: jax.Array def _kbi( @@ -59,13 +56,13 @@ def _kbi( timeconst, dampratio = solref if not m.opt.disableflags & DisableBit.REFSAFE: - timeconst = jp.maximum(timeconst, 2 * m.opt.timestep) * (timeconst > 0) + timeconst = jp.maximum(timeconst, 2 * m.opt.timestep) dmin, dmax, width, mid, power = solimp dmin = jp.clip(dmin, mujoco.mjMINIMP, mujoco.mjMAXIMP) dmax = jp.clip(dmax, mujoco.mjMINIMP, mujoco.mjMAXIMP) - width = jp.maximum(0, width) + width = jp.maximum(mujoco.mjMINVAL, width) mid = jp.clip(mid, mujoco.mjMINIMP, mujoco.mjMAXIMP) power = jp.maximum(1, power) @@ -73,8 +70,8 @@ def _kbi( k = 1 / (dmax * dmax * timeconst * timeconst * dampratio * dampratio) b = 2 / (dmax * timeconst) # TODO(robotics-simulation): check various solparam settings in model gen test - k = jp.where(dampratio <= 0, -solref[0] / (dmax * dmax), k) - b = jp.where(timeconst <= 0, -solref[1] / dmax, b) + k = jp.where(solref[0] <= 0, -solref[0] / (dmax * dmax), k) + b = jp.where(solref[1] <= 0, -solref[1] / dmax, b) imp_x = jp.abs(pos) / width imp_a = (1.0 / jp.power(mid, power - 1)) * jp.power(imp_x, power) @@ -87,254 +84,280 @@ def _kbi( return k, b, imp # corresponds to K, B, I of efc_KBIP -def _instantiate_equality_connect(m: Model, d: Data) -> Optional[_Efc]: +def _row(j: jax.Array, *args) -> _Efc: + """Creates an efc row, ensuring args all have same row count.""" + if len(j.shape) < 2: + return _Efc(j, *args) # if j isn't batched, ignore + + args = list(args) + for i, arg in enumerate(args): + if not arg.shape or arg.shape[0] != j.shape[0]: + args[i] = jp.tile(arg, (j.shape[0],) + (1,) * (len(arg.shape))) + return _Efc(j, *args) + + +def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]: """Calculates constraint rows for connect equality constraints.""" - ids = np.nonzero(m.eq_type == EqType.CONNECT)[0] - - if (m.opt.disableflags & DisableBit.EQUALITY) or ids.size == 0: + eq_id = np.nonzero(m.eq_type == EqType.CONNECT)[0] + if (m.opt.disableflags & DisableBit.EQUALITY) or eq_id.size == 0: return None - id1, id2, data = m.eq_obj1id[ids], m.eq_obj2id[ids], m.eq_data[ids] - @jax.vmap - def fn(data, id1, id2): + def rows(obj1id, obj2id, data, solref, solimp): anchor1, anchor2 = data[0:3], data[3:6] - # find global points - pos1 = d.xmat[id1] @ anchor1 + d.xpos[id1] - pos2 = d.xmat[id2] @ anchor2 + d.xpos[id2] - # compute position error - cpos = pos1 - pos2 + # error is difference in global positions + pos1 = d.xmat[obj1id] @ anchor1 + d.xpos[obj1id] + pos2 = d.xmat[obj2id] @ anchor2 + d.xpos[obj2id] + pos = pos1 - pos2 # compute Jacobian difference (opposite of contact: 0 - 1) - jacp1, _ = support.jac(m, d, pos1, id1) - jacp2, _ = support.jac(m, d, pos2, id2) + jacp1, _ = support.jac(m, d, pos1, obj1id) + jacp2, _ = support.jac(m, d, pos2, obj2id) j = (jacp1 - jacp2).T + pos_imp = math.norm(pos) + invweight = m.body_invweight0[obj1id, 0] + m.body_invweight0[obj2id, 0] - return j, cpos, jp.repeat(math.norm(cpos), 3) + return _row(j, pos, pos_imp, invweight, solref, solimp) - # concatenate to drop connect grouping dimension - j, pos, pos_norm = jax.tree_util.tree_map(jp.concatenate, fn(data, id1, id2)) - invweight = m.body_invweight0[id1, 0] + m.body_invweight0[id2, 0] - invweight = jp.repeat(invweight, 3) - solref = jp.tile(m.eq_solref[ids], (3, 1)) - solimp = jp.tile(m.eq_solimp[ids], (3, 1)) - frictionloss = jp.zeros_like(pos_norm) - - return _Efc(j, pos, pos_norm, invweight, solref, solimp, frictionloss) + args = (m.eq_obj1id, m.eq_obj2id, m.eq_data, m.eq_solref, m.eq_solimp) + args = jax.tree_util.tree_map(lambda x: x[eq_id], args) + # concatenate to drop row grouping + return jax.tree_util.tree_map(jp.concatenate, rows(*args)) -def _instantiate_equality_weld(m: Model, d: Data) -> Optional[_Efc]: +def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]: """Calculates constraint rows for weld equality constraints.""" - ids = np.nonzero(m.eq_type == EqType.WELD)[0] - - if (m.opt.disableflags & DisableBit.EQUALITY) or ids.size == 0: + eq_id = np.nonzero(m.eq_type == EqType.WELD)[0] + if (m.opt.disableflags & DisableBit.EQUALITY) or eq_id.size == 0: return None - id1, id2, data = m.eq_obj1id[ids], m.eq_obj2id[ids], m.eq_data[ids] - @jax.vmap - def fn(data, id1, id2): + def rows(obj1id, obj2id, data, solref, solimp): anchor1, anchor2 = data[0:3], data[3:6] relpose, torquescale = data[6:10], data[10] - # find global points - pos1 = d.xmat[id1] @ anchor2 + d.xpos[id1] - pos2 = d.xmat[id2] @ anchor1 + d.xpos[id2] - - # compute position error + # error is difference in global position and orientation + pos1 = d.xmat[obj1id] @ anchor2 + d.xpos[obj1id] + pos2 = d.xmat[obj2id] @ anchor1 + d.xpos[obj2id] cpos = pos1 - pos2 # compute Jacobian difference (opposite of contact: 0 - 1) - jacp1, jacr1 = support.jac(m, d, pos1, id1) - jacp2, jacr2 = support.jac(m, d, pos2, id2) + jacp1, jacr1 = support.jac(m, d, pos1, obj1id) + jacp2, jacr2 = support.jac(m, d, pos2, obj2id) jacdifp = jacp1 - jacp2 jacdifr = (jacr1 - jacr2) * torquescale # compute orientation error: neg(q1) * q0 * relpose (axis components only) - quat = math.quat_mul(d.xquat[id1], relpose) - quat1 = math.quat_inv(d.xquat[id2]) + quat = math.quat_mul(d.xquat[obj1id], relpose) + quat1 = math.quat_inv(d.xquat[obj2id]) crot = math.quat_mul(quat1, quat)[1:] # copy axis components + pos = jp.concatenate((cpos, crot * torquescale)) + # correct rotation Jacobian: 0.5 * neg(q1) * (jac0-jac1) * q0 * relpose jac_fn = lambda j: math.quat_mul(math.quat_mul_axis(quat1, j), quat)[1:] jacdifr = 0.5 * jax.vmap(jac_fn)(jacdifr) - j = jp.concatenate((jacdifp.T, jacdifr.T)) - pos = jp.concatenate((cpos, crot * torquescale)) + pos_imp = math.norm(pos) + invweight = m.body_invweight0[obj1id] + m.body_invweight0[obj2id] + invweight = jp.repeat(invweight, 3, axis=0) - return j, pos, jp.repeat(math.norm(pos), 6) + return _row(j, pos, pos_imp, invweight, solref, solimp) - # concatenate to drop weld grouping dimension - j, pos, pos_norm = jax.tree_util.tree_map(jp.concatenate, fn(data, id1, id2)) - invweight = m.body_invweight0[id1] + m.body_invweight0[id2] - invweight = jp.repeat(invweight, 3) - solref = jp.tile(m.eq_solref[ids], (6, 1)) - solimp = jp.tile(m.eq_solimp[ids], (6, 1)) - frictionloss = jp.zeros_like(pos_norm) - - return _Efc(j, pos, pos_norm, invweight, solref, solimp, frictionloss) + args = (m.eq_obj1id, m.eq_obj2id, m.eq_data, m.eq_solref, m.eq_solimp) + args = jax.tree_util.tree_map(lambda x: x[eq_id], args) + # concatenate to drop row grouping + return jax.tree_util.tree_map(jp.concatenate, rows(*args)) -def _instantiate_equality_joint(m: Model, d: Data) -> Optional[_Efc]: +def _efc_equality_joint(m: Model, d: Data) -> Optional[_Efc]: """Calculates constraint rows for joint equality constraints.""" - ids = np.nonzero(m.eq_type == EqType.JOINT)[0] + eq_id = np.nonzero(m.eq_type == EqType.JOINT)[0] - if (m.opt.disableflags & DisableBit.EQUALITY) or ids.size == 0: + if (m.opt.disableflags & DisableBit.EQUALITY) or eq_id.size == 0: return None - id1, id2, data = m.eq_obj1id[ids], m.eq_obj2id[ids], m.eq_data[ids] - dofadr1, dofadr2 = m.jnt_dofadr[id1], m.jnt_dofadr[id2] - qposadr1, qposadr2 = m.jnt_qposadr[id1], m.jnt_qposadr[id2] - @jax.vmap - def fn(data, id2, dofadr1, dofadr2, qposadr1, qposadr2): + def rows(obj2id, data, solref, solimp, dofadr1, dofadr2, qposadr1, qposadr2): pos1, pos2 = d.qpos[qposadr1], d.qpos[qposadr2] ref1, ref2 = m.qpos0[qposadr1], m.qpos0[qposadr2] - pos2, ref2 = pos2 * (id2 > -1), ref2 * (id2 > -1) - - dif = pos2 - ref2 + dif = (pos2 - ref2) * (obj2id > -1) dif_power = jp.power(dif, jp.arange(0, 5)) - - deriv = jp.dot(data[1:5], dif_power[:4] * jp.arange(1, 5)) - j = jp.zeros((m.nv)).at[dofadr1].set(1.0).at[dofadr2].set(-deriv) pos = pos1 - ref1 - jp.dot(data[:5], dif_power) - return j, pos + deriv = jp.dot(data[1:5], dif_power[:4] * jp.arange(1, 5)) * (obj2id > -1) - j, pos = fn(data, id2, dofadr1, dofadr2, qposadr1, qposadr2) - invweight = m.dof_invweight0[dofadr1] + m.dof_invweight0[dofadr2] * (id2 > -1) - solref, solimp = m.eq_solref[ids], m.eq_solimp[ids] - frictionloss = jp.zeros_like(pos) + j = jp.zeros((m.nv)).at[dofadr2].set(-deriv).at[dofadr1].set(1.0) + invweight = m.dof_invweight0[dofadr1] + invweight += m.dof_invweight0[dofadr2] * (obj2id > -1) - return _Efc(j, pos, pos, invweight, solref, solimp, frictionloss) + return _row(j, pos, pos, invweight, solref, solimp) + + args = (m.eq_obj1id, m.eq_obj2id, m.eq_data, m.eq_solref, m.eq_solimp) + args = jax.tree_util.tree_map(lambda x: x[eq_id], args) + dofadr1, dofadr2 = m.jnt_dofadr[args[0]], m.jnt_dofadr[args[1]] + qposadr1, qposadr2 = m.jnt_qposadr[args[0]], m.jnt_qposadr[args[1]] + args = args[1:] + (dofadr1, dofadr2, qposadr1, qposadr2) + + return rows(*args) -def _instantiate_friction(m: Model, d: Data) -> Optional[_Efc]: +def _efc_friction(m: Model, d: Data) -> Optional[_Efc]: # TODO(robotics-team): implement _instantiate_friction del m, d return None -def _instantiate_limit_ball(m: Model, d: Data) -> Optional[_Efc]: +def _efc_limit_ball(m: Model, d: Data) -> Optional[_Efc]: """Calculates constraint rows for ball joint limits.""" - ids = np.nonzero((m.jnt_type == JointType.BALL) & m.jnt_limited)[0] + jnt_id = np.nonzero((m.jnt_type == JointType.BALL) & m.jnt_limited)[0] - if (m.opt.disableflags & DisableBit.LIMIT) or ids.size == 0: + if (m.opt.disableflags & DisableBit.LIMIT) or jnt_id.size == 0: return None - jnt_range = m.jnt_range[ids] - jnt_margin = m.jnt_margin[ids] - qposadr = np.array([np.arange(q, q + 4) for q in m.jnt_qposadr[ids]]) - dofadr = np.array([np.arange(d, d + 3) for d in m.jnt_dofadr[ids]]) - @jax.vmap - def fn(jnt_range, jnt_margin, qposadr, dofadr): - axis, angle = math.quat_to_axis_angle(d.qpos[qposadr]) - j = jp.zeros(m.nv).at[dofadr].set(-axis) + def rows(qposadr, dofadr, jnt_range, jnt_margin, solref, solimp): + axis, angle = math.quat_to_axis_angle(d.qpos[jp.arange(4) + qposadr]) pos = jp.amax(jnt_range) - angle - jnt_margin active = pos < 0 - return j * active, pos * active + j = jp.zeros(m.nv).at[jp.arange(3) + dofadr].set(-axis) + invweight = m.dof_invweight0[dofadr] - j, pos = fn(jnt_range, jnt_margin, qposadr, dofadr) - invweight = m.dof_invweight0[m.jnt_dofadr[ids]] - solref, solimp = m.jnt_solref[ids], m.jnt_solimp[ids] - frictionloss = jp.zeros_like(pos) + return _row(j * active, pos * active, pos, invweight, solref, solimp) - return _Efc(j, pos, pos, invweight, solref, solimp, frictionloss) + args = (m.jnt_qposadr, m.jnt_dofadr, m.jnt_range, m.jnt_margin, m.jnt_solref) + args += (m.jnt_solimp,) + args = jax.tree_util.tree_map(lambda x: x[jnt_id], args) + + return rows(*args) -def _instantiate_limit_slide_hinge(m: Model, d: Data) -> Optional[_Efc]: +def _efc_limit_slide_hinge(m: Model, d: Data) -> Optional[_Efc]: """Calculates constraint rows for slide and hinge joint limits.""" slide_hinge = np.isin(m.jnt_type, (JointType.SLIDE, JointType.HINGE)) - ids = np.nonzero(slide_hinge & m.jnt_limited)[0] + jnt_id = np.nonzero(slide_hinge & m.jnt_limited)[0] - if (m.opt.disableflags & DisableBit.LIMIT) or ids.size == 0: + if (m.opt.disableflags & DisableBit.LIMIT) or jnt_id.size == 0: return None - jnt_range = m.jnt_range[ids] - jnt_margin = m.jnt_margin[ids] - qposadr = m.jnt_qposadr[ids] - dofadr = m.jnt_dofadr[ids] - @jax.vmap - def fn(jnt_range, jnt_margin, qposadr, dofadr): - dist_min = d.qpos[qposadr] - jnt_range[0] - dist_max = jnt_range[1] - d.qpos[qposadr] - j = jp.zeros(m.nv).at[dofadr].set((dist_min < dist_max) * 2 - 1) + def rows(qposadr, dofadr, jnt_range, jnt_margin, solref, solimp): + qpos = d.qpos[qposadr] + dist_min, dist_max = qpos - jnt_range[0], jnt_range[1] - qpos pos = jp.minimum(dist_min, dist_max) - jnt_margin active = pos < 0 - return j * active, pos * active + j = jp.zeros(m.nv).at[dofadr].set((dist_min < dist_max) * 2 - 1) + invweight = m.dof_invweight0[dofadr] - j, pos = fn(jnt_range, jnt_margin, qposadr, dofadr) - invweight = m.dof_invweight0[dofadr] - solref, solimp = m.jnt_solref[ids], m.jnt_solimp[ids] - frictionloss = jp.zeros_like(pos) + return _row(j * active, pos * active, pos, invweight, solref, solimp) - return _Efc(j, pos, pos, invweight, solref, solimp, frictionloss) + args = (m.jnt_qposadr, m.jnt_dofadr, m.jnt_range, m.jnt_margin, m.jnt_solref) + args += (m.jnt_solimp,) + args = jax.tree_util.tree_map(lambda x: x[jnt_id], args) + + return rows(*args) -def _instantiate_contact(m: Model, d: Data) -> Optional[_Efc]: - """Calculates constraint rows for contacts.""" +def _efc_contact_frictionless(m: Model, d: Data) -> Optional[_Efc]: + """Calculates constraint rows for frictionless contacts.""" - if d.ncon == 0: + con_id = np.nonzero(d.contact.dim == 1)[0] + + if con_id.size == 0: return None - def contact_efc(c: Contact, condim: int): + @jax.vmap + def rows(c: Contact): + pos = c.dist - c.includemargin + active = pos < 0 + body1, body2 = jp.array(m.geom_bodyid)[c.geom] + jac1p, _ = support.jac(m, d, c.pos, body1) + jac2p, _ = support.jac(m, d, c.pos, body2) + j = (c.frame @ (jac2p - jac1p).T)[0] + invweight = m.body_invweight0[body1, 0] + m.body_invweight0[body2, 0] - @jax.vmap - def fn(c: Contact): - dist = c.dist - c.includemargin - active = dist < 0 - body1, body2 = jp.array(m.geom_bodyid)[c.geom] - jac1p, jac1r = support.jac(m, d, c.pos, body1) - jac2p, jac2r = support.jac(m, d, c.pos, body2) - diff = c.frame @ (jac2p - jac1p).T - if condim > 3: # only calculate rotational diff if needed - diff = jp.concatenate((diff, c.frame @ (jac2r - jac1r).T), axis=0) - tran = m.body_invweight0[body1, 0] + m.body_invweight0[body2, 0] + return _row(j * active, pos * active, pos, invweight, c.solref, c.solimp) - if condim == 1: - return diff[0] * active, tran, dist * active, c.solref, c.solimp + contact = jax.tree_util.tree_map(lambda x: x[con_id], d.contact) - # a pair of opposing pyramid edges per friction dimension - # repeat friction directions with positive and negative sign - fri = jp.repeat(c.friction[: condim - 1], 2, axis=0).at[1::2].mul(-1) - # repeat condims of jacdiff to match +/- friction directions - j = diff[0] + jp.repeat(diff[1:condim], 2, axis=0) * fri[:, None] - # pyramidal has common invweight across all edges - diag_approx = tran + fri[0] * fri[0] * tran - inv_w = diag_approx * 2 * fri[0] * fri[0] / m.opt.impratio - repeat_fn = lambda x: jp.repeat(x[None], (condim - 1) * 2, axis=0) - inv_w, pos, solref, solimp = jax.tree_util.tree_map( - repeat_fn, (inv_w, dist, c.solref, c.solimp) - ) - return j * active, inv_w, pos * active, solref, solimp + return rows(contact) - return fn(c) - # group efc calculations by condim - dims, begs = np.unique(d.contact.dim, return_index=True) - efcs = [] - for i in range(len(dims)): - dim, beg = dims[i], begs[i] - end = begs[i + 1] if i < len(dims) - 1 else None - c = jax.tree_util.tree_map(lambda x, b=beg, e=end: x[b:e], d.contact) - efc = contact_efc(c, dim) - if dim > 1: - # remove efc grouping dimension - efc = jax.tree_util.tree_map(jp.concatenate, efc) - efcs.append(efc) +def _efc_contact_pyramidal(m: Model, d: Data, condim: int) -> Optional[_Efc]: + """Calculates constraint rows for frictional pyramidal contacts.""" - efc = jax.tree_util.tree_map(lambda *x: jp.concatenate(x), *efcs) - j, invweight, pos, solref, solimp = efc - frictionloss = jp.zeros_like(pos) + con_id = np.nonzero(d.contact.dim == condim)[0] - return _Efc(j, pos, pos, invweight, solref, solimp, frictionloss) + if con_id.size == 0: + return None + + @jax.vmap + def rows(c: Contact): + pos = c.dist - c.includemargin + active = pos < 0 + body1, body2 = jp.array(m.geom_bodyid)[c.geom] + jac1p, jac1r = support.jac(m, d, c.pos, body1) + jac2p, jac2r = support.jac(m, d, c.pos, body2) + diff = c.frame @ (jac2p - jac1p).T + if condim > 3: + diff = jp.concatenate((diff, (c.frame @ (jac2r - jac1r).T)), axis=0) + # a pair of opposing pyramid edges per friction dimension + # repeat friction directions with positive and negative sign + fri = jp.repeat(c.friction[: condim - 1], 2, axis=0).at[1::2].mul(-1) + # repeat condims of jacdiff to match +/- friction directions + j = diff[0] + jp.repeat(diff[1:condim], 2, axis=0) * fri[:, None] + + # pyramidal has common invweight across all edges + invweight = m.body_invweight0[body1, 0] + m.body_invweight0[body2, 0] + invweight = invweight + fri[0] * fri[0] * invweight + invweight = invweight * 2 * fri[0] * fri[0] / m.opt.impratio + + return _row(j * active, pos * active, pos, invweight, c.solref, c.solimp) + + contact = jax.tree_util.tree_map(lambda x: x[con_id], d.contact) + # concatenate to drop row grouping + return jax.tree_util.tree_map(jp.concatenate, rows(contact)) + + +def _efc_contact_elliptic(m: Model, d: Data, condim: int) -> Optional[_Efc]: + """Calculates constraint rows for frictional elliptic contacts.""" + + con_id = np.nonzero(d.contact.dim == condim)[0] + + if con_id.size == 0: + return None + + @jax.vmap + def rows(c: Contact): + pos = c.dist - c.includemargin + active = pos < 0 + obj1id, obj2id = jp.array(m.geom_bodyid)[c.geom] + jac1p, jac1r = support.jac(m, d, c.pos, obj1id) + jac2p, jac2r = support.jac(m, d, c.pos, obj2id) + j = c.frame @ (jac2p - jac1p).T + if condim > 3: + j = jp.concatenate((j, (c.frame @ (jac2r - jac1r).T)[: condim - 3])) + invweight = m.body_invweight0[obj1id, 0] + m.body_invweight0[obj2id, 0] + + # normal row comes from solref, remaining rows from solreffriction + solreffriction = c.solreffriction + c.solref * ~c.solreffriction.any() + solreffriction = jp.tile(solreffriction, (condim - 1, 1)) + solref = jp.concatenate((c.solref[None], solreffriction)) + fri = jp.square(c.friction[0]) / jp.square(c.friction[1 : condim - 1]) + invweight = jp.array([invweight, invweight / m.opt.impratio]) + invweight = jp.concatenate((invweight, invweight[1] * fri)) + pos_aref = jp.zeros(condim).at[0].set(pos) + + return _row(j * active, pos_aref * active, pos, invweight, solref, c.solimp) + + contact = jax.tree_util.tree_map(lambda x: x[con_id], d.contact) + # concatenate to drop row grouping + return jax.tree_util.tree_map(jp.concatenate, rows(contact)) def counts(efc_type: np.ndarray) -> Tuple[int, int, int, int]: @@ -344,7 +367,8 @@ def counts(efc_type: np.ndarray) -> Tuple[int, int, int, int]: nl = (efc_type == ConstraintType.LIMIT_JOINT).sum() nc_f = (efc_type == ConstraintType.CONTACT_FRICTIONLESS).sum() nc_p = (efc_type == ConstraintType.CONTACT_PYRAMIDAL).sum() - nc = nc_f + nc_p + nc_e = (efc_type == ConstraintType.CONTACT_ELLIPTIC).sum() + nc = nc_f + nc_p + nc_e return ne, nf, nl, nc @@ -363,25 +387,48 @@ def make_efc_type( num_rows = (m.eq_type == EqType.CONNECT).sum() * 3 num_rows += (m.eq_type == EqType.WELD).sum() * 6 num_rows += (m.eq_type == EqType.JOINT).sum() - efc_types.extend([ConstraintType.EQUALITY] * num_rows) + efc_types += [ConstraintType.EQUALITY] * num_rows if not m.opt.disableflags & DisableBit.LIMIT: - efc_types.extend([ConstraintType.LIMIT_JOINT] * m.jnt_limited.sum()) + efc_types += [ConstraintType.LIMIT_JOINT] * m.jnt_limited.sum() if not m.opt.disableflags & DisableBit.CONTACT: - num_rows = sum(_CONDIM_EFC_COUNT[d] for d in dim) - efc_types.extend([ConstraintType.CONTACT_PYRAMIDAL] * num_rows) + for condim in (1, 3, 4, 6): + n = (dim == condim).sum() + if condim == 1: + efc_types += [ConstraintType.CONTACT_FRICTIONLESS] * n + elif m.opt.cone == ConeType.PYRAMIDAL: + efc_types += [ConstraintType.CONTACT_PYRAMIDAL] * (condim - 1) * 2 * n + elif m.opt.cone == ConeType.ELLIPTIC: + efc_types += [ConstraintType.CONTACT_ELLIPTIC] * condim * n + else: + raise ValueError(f'Unknown cone: {m.opt.cone}') return np.array(efc_types) -def make_efc_address(efc_type: np.ndarray, dim: np.ndarray) -> np.ndarray: +def make_efc_address( + m: Union[Model, mujoco.MjModel], dim: np.ndarray, efc_type: np.ndarray +) -> np.ndarray: """Returns efc_address that maps contacts to constraint row address.""" - nc = (efc_type == ConstraintType.CONTACT_PYRAMIDAL).sum() - nc_start = efc_type.size - nc - offsets = np.cumsum([0] + [_CONDIM_EFC_COUNT[d] for d in dim])[:-1] + offsets = np.array([0], dtype=int) + for condim in (1, 3, 4, 6): + n = (dim == condim).sum() + if n == 0: + continue + if condim == 1: + offsets = np.concatenate((offsets, [1] * n)) + elif m.opt.cone == ConeType.PYRAMIDAL: + offsets = np.concatenate((offsets, [(condim - 1) * 2] * n)) + elif m.opt.cone == ConeType.ELLIPTIC: + offsets = np.concatenate((offsets, [condim] * n)) + else: + raise ValueError(f'Unknown cone: {m.opt.cone}') - return nc_start + offsets + _, _, _, nc = counts(efc_type) + address = efc_type.size - nc + np.cumsum(offsets)[:-1] + + return address def make_constraint(m: Model, d: Data) -> Data: @@ -390,15 +437,21 @@ def make_constraint(m: Model, d: Data) -> Data: if m.opt.disableflags & DisableBit.CONSTRAINT: efcs = () else: - efcs = tuple(efc for efc in ( - _instantiate_equality_connect(m, d), - _instantiate_equality_weld(m, d), - _instantiate_equality_joint(m, d), - _instantiate_friction(m, d), - _instantiate_limit_ball(m, d), - _instantiate_limit_slide_hinge(m, d), - _instantiate_contact(m, d), - ) if efc is not None) + efcs = ( + _efc_equality_connect(m, d), + _efc_equality_weld(m, d), + _efc_equality_joint(m, d), + _efc_friction(m, d), + _efc_limit_ball(m, d), + _efc_limit_slide_hinge(m, d), + _efc_contact_frictionless(m, d), + ) + if m.opt.cone == ConeType.ELLIPTIC: + con_fn = _efc_contact_elliptic + else: + con_fn = _efc_contact_pyramidal + efcs += tuple(con_fn(m, d, dim) for dim in (3, 4, 6)) + efcs = tuple(efc for efc in efcs if efc is not None) if not efcs: z = jp.empty(0) @@ -410,13 +463,13 @@ def make_constraint(m: Model, d: Data) -> Data: @jax.vmap def fn(efc): - k, b, imp = _kbi(m, efc.solref, efc.solimp, efc.pos_norm) + k, b, imp = _kbi(m, efc.solref, efc.solimp, efc.pos_imp) r = jp.maximum(efc.invweight * (1 - imp) / imp, mujoco.mjMINVAL) - aref = -b * (efc.J @ d.qvel) - k * imp * efc.pos + aref = -b * (efc.J @ d.qvel) - k * imp * efc.pos_aref return aref, r aref, r = fn(efc) d = d.replace(efc_J=efc.J, efc_D=1 / r, efc_aref=aref) - d = d.replace(efc_frictionloss=efc.frictionloss) + d = d.replace(efc_frictionloss=jp.zeros_like(r)) return d diff --git a/mjx/mujoco/mjx/_src/constraint_test.py b/mjx/mujoco/mjx/_src/constraint_test.py index 124227b4..f7cc6530 100644 --- a/mjx/mujoco/mjx/_src/constraint_test.py +++ b/mjx/mujoco/mjx/_src/constraint_test.py @@ -15,6 +15,7 @@ """Tests for constraint functions.""" from absl.testing import absltest +from absl.testing import parameterized from jax import numpy as jp import mujoco from mujoco import mjx @@ -38,41 +39,32 @@ def _assert_attr_eq(a, b, attr): _assert_eq(getattr(a, attr), getattr(b, attr), attr) -class ConstraintTest(absltest.TestCase): +class ConstraintTest(parameterized.TestCase): - def test_constraints(self): + @parameterized.parameters( + mujoco.mjtCone.mjCONE_PYRAMIDAL, mujoco.mjtCone.mjCONE_ELLIPTIC + ) + def test_constraints(self, cone): """Test constraints.""" m = test_util.load_test_file('constraints.xml') + m.opt.cone = cone d = mujoco.MjData(m) - mujoco.mj_step(m, d, 100) # at 100 steps mix of active/inactive constraints - mujoco.mj_forward(m, d) - mx = mjx.put_model(m) - dx = mjx.put_data(m, d) - dx = mjx.make_constraint(mx, dx) - d_efc_j = d.efc_J.reshape((-1, m.nv)) - # ne, nf, nl order matches - efl = d.ne + d.nf + d.nl - _assert_eq(d_efc_j[:efl], dx.efc_J[:efl], 'efc_J') - _assert_eq(d.efc_D[:efl], dx.efc_D[:efl], 'efc_D') - _assert_eq(d.efc_aref[:efl], dx.efc_aref[:efl], 'efc_aref') - _assert_eq(dx.efc_frictionloss, 0, 'efc_frictionloss') + # sample a mix of active/inactive constraints at different timesteps + for key in range(3): + mujoco.mj_resetDataKeyframe(m, d, key) + mujoco.mj_forward(m, d) + mx = mjx.put_model(m) + dx = mjx.put_data(m, d) + dx = mjx.make_constraint(mx, dx) - # contact order might not match, so check efcs contact by contact - for i in range(d.ncon): - geom_match = (dx.contact.geom == d.contact.geom[i]).all(axis=-1) - geom_match &= (dx.contact.pos == d.contact.pos[i]).all(axis=-1) - self.assertTrue(geom_match.any(), f'contact {i} not found in MJX contact') - j = np.nonzero(geom_match)[0][0] - self.assertEqual(d.contact.dim[i], dx.contact.dim[j]) - nc = max(1, (d.contact.dim[i] - 1) * 2) - d_beg, dx_beg = d.contact.efc_address[i], dx.contact.efc_address[j] - d_end, dx_end = d_beg + nc, dx_beg + nc - _assert_eq(d_efc_j[d_beg:d_end], dx.efc_J[dx_beg:dx_end], 'efc_J') - _assert_eq(d.efc_D[d_beg:d_end], dx.efc_D[dx_beg:dx_end], 'efc_D') - d_efc_aref = d.efc_aref[d_beg:d_end] - dx_efc_aref = dx.efc_aref[dx_beg:dx_end] - _assert_eq(d_efc_aref, dx_efc_aref, 'efc_aref') + order = test_util.efc_order(m, d, dx) + d_efc_j = d.efc_J.reshape((-1, m.nv)) + _assert_eq(d_efc_j, dx.efc_J[order][:d.nefc], 'efc_J') + _assert_eq(0, dx.efc_J[order][d.nefc:], 'efc_J') + _assert_eq(d.efc_aref, dx.efc_aref[order][:d.nefc], 'efc_aref') + _assert_eq(0, dx.efc_aref[order][d.nefc:], 'efc_aref') + _assert_eq(d.efc_D, dx.efc_D[order][:d.nefc], 'efc_D') def test_disable_refsafe(self): m = test_util.load_test_file('constraints.xml') diff --git a/mjx/mujoco/mjx/_src/io.py b/mjx/mujoco/mjx/_src/io.py index 5068373a..564020f6 100644 --- a/mjx/mujoco/mjx/_src/io.py +++ b/mjx/mujoco/mjx/_src/io.py @@ -15,7 +15,7 @@ """Functions to initialize, load, or save data.""" import copy -from typing import List, Union +from typing import List, Tuple, Union import jax from jax import numpy as jp @@ -133,7 +133,7 @@ def make_data(m: Union[types.Model, mujoco.MjModel]) -> types.Data: """Allocate and initialize Data.""" dim = collision_driver.make_condim(m) efc_type = constraint.make_efc_type(m, dim) - efc_address = constraint.make_efc_address(efc_type, dim) + efc_address = constraint.make_efc_address(m, dim, efc_type) ne, nf, nl, nc = constraint.counts(efc_type) ncon, nefc = dim.size, ne + nf + nl + nc @@ -330,7 +330,7 @@ def _make_contact( c: mujoco._structs._MjContactList, dim: np.ndarray, efc_address: np.ndarray, -) -> types.Contact: +) -> Tuple[types.Contact, np.ndarray]: """Converts mujoco.structs._MjContactList into mjx.Contact.""" fields = {f.name: getattr(c, f.name) for f in types.Contact.fields()} fields['frame'] = fields['frame'].reshape((-1, 3, 3)) @@ -351,21 +351,21 @@ def _make_contact( zero = jax.tree_util.tree_map( lambda x: np.zeros((1,) + x.shape[1:], dtype=x.dtype), fields ) - zero['dist'][:] = np.finfo(float).max + zero['dist'][:] = 1e10 fields = jax.tree_util.tree_map(lambda *x: np.concatenate(x), fields, zero) fields = jax.tree_util.tree_map(lambda x: x[contact_map], fields) fields['dim'] = dim fields['efc_address'] = efc_address - return types.Contact(**fields) + return types.Contact(**fields), contact_map def put_data(m: mujoco.MjModel, d: mujoco.MjData, device=None) -> types.Data: """Puts mujoco.MjData onto a device, resulting in mjx.Data.""" dim = collision_driver.make_condim(m) efc_type = constraint.make_efc_type(m, dim) - efc_address = constraint.make_efc_address(efc_type, dim) + efc_address = constraint.make_efc_address(m, dim, efc_type) ne, nf, nl, nc = constraint.counts(efc_type) ncon, nefc = dim.size, ne + nf + nl + nc @@ -388,6 +388,8 @@ def put_data(m: mujoco.MjModel, d: mujoco.MjData, device=None) -> types.Data: # MJX does not support islanding, so only transfer the first solver_niter fields['solver_niter'] = fields['solver_niter'][0] + contact, contact_map = _make_contact(d.contact, dim, efc_address) + # pad efc fields: MuJoCo efc arrays are sparse for inactive constraints. # efc_J is also optionally column-sparse (typically for large nv). MJX is # neither: it contains zeros for inactive constraints, and efc_J is always @@ -403,13 +405,25 @@ def put_data(m: mujoco.MjModel, d: mujoco.MjData, device=None) -> types.Data: else: fields['efc_J'] = fields['efc_J'].reshape((-1 if m.nv else 0, m.nv)) + # move efc rows to their correct offsets for fname in ('efc_J', 'efc_frictionloss', 'efc_D', 'efc_aref', 'efc_force'): value = np.zeros((nefc, m.nv)) if fname == 'efc_J' else np.zeros(nefc) - for i in range(4): - value_beg = sum([ne, nf, nl][:i]) - d_beg = sum([d.ne, d.nf, d.nl][:i]) - size = [d.ne, d.nf, d.nl, d.nefc - d.nl - d.nf - d.ne][i] + for i in range(3): + value_beg = sum([ne, nf][:i]) + d_beg = sum([d.ne, d.nf][:i]) + size = [d.ne, d.nf, d.nl][i] value[value_beg : value_beg + size] = fields[fname][d_beg : d_beg + size] + + # for nc, we may reorder contacts so they match MJX order: group by dim + for id_to, id_from in enumerate(contact_map): + if id_from == -1: + continue + num_rows = dim[id_to] + if num_rows > 1 and m.opt.cone == mujoco.mjtCone.mjCONE_PYRAMIDAL: + num_rows = (num_rows - 1) * 2 + efc_i, efc_o = d.contact.efc_address[id_from], efc_address[id_to] + value[efc_o:efc_o + num_rows] = fields[fname][efc_i:efc_i + num_rows] + fields[fname] = value # convert qM and qLD if jacobian is dense @@ -424,7 +438,7 @@ def put_data(m: mujoco.MjModel, d: mujoco.MjData, device=None) -> types.Data: fields['qLD'] = np.zeros((m.nv, m.nv)) fields['qLDiagInv'] = np.zeros(0) - fields['contact'] = _make_contact(d.contact, dim, efc_address) + fields['contact'] = contact fields.update(ne=ne, nf=nf, nl=nl, nefc=nefc, ncon=ncon, efc_type=efc_type) # copy because device_put is async: diff --git a/mjx/mujoco/mjx/_src/io_test.py b/mjx/mujoco/mjx/_src/io_test.py index 525db2bb..bb540a5d 100644 --- a/mjx/mujoco/mjx/_src/io_test.py +++ b/mjx/mujoco/mjx/_src/io_test.py @@ -140,14 +140,6 @@ class ModelIOTest(parameterized.TestCase): ) ) - def test_cone_not_implemented(self): - with self.assertRaises(NotImplementedError): - mjx.put_model( - mujoco.MjModel.from_xml_string( - '' - ) - ) - def test_pgs_not_implemented(self): with self.assertRaises(NotImplementedError): mjx.put_model( @@ -299,7 +291,7 @@ class DataIOTest(parameterized.TestCase): self.assertEqual(dx.contact.dist.shape, (4,)) self.assertEqual(d.ncon, 1) # however only 1 contact in this step np.testing.assert_allclose(dx.contact.dist[0], d.contact.dist[0]) - self.assertTrue(np.isinf(dx.contact.dist[1:]).all()) + self.assertTrue((dx.contact.dist[1:] > 0).all()) self.assertEqual(dx.contact.frame.shape, (4, 3, 3)) np.testing.assert_allclose( dx.contact.frame[0].reshape(9), d.contact.frame[0] diff --git a/mjx/mujoco/mjx/_src/solver.py b/mjx/mujoco/mjx/_src/solver.py index 195da43a..31004f44 100644 --- a/mjx/mujoco/mjx/_src/solver.py +++ b/mjx/mujoco/mjx/_src/solver.py @@ -22,6 +22,7 @@ from mujoco.mjx._src import smooth from mujoco.mjx._src import support # pylint: disable=g-importing-member from mujoco.mjx._src.dataclasses import PyTreeNode +from mujoco.mjx._src.types import ConeType from mujoco.mjx._src.types import Data from mujoco.mjx._src.types import DisableBit from mujoco.mjx._src.types import Model @@ -45,8 +46,12 @@ class _Context(PyTreeNode): cost: constraint + Gauss cost prev_cost: cost from previous iter solver_niter: number of solver iterations + active: active (quadratic) constraints (nefc,) + fri: friction of regularized cone (num(con.dim > 1), 6) + dm: regularized constraint mass (num(con.dim > 1)) + u: friction cone (normal and tangents) (num(con.dim > 1), 6) + h: cone hessian (num(con.dim > 1), 6, 6) """ - qacc: jax.Array qfrc_constraint: jax.Array Jaref: jax.Array # pylint: disable=invalid-name @@ -59,6 +64,11 @@ class _Context(PyTreeNode): cost: jax.Array prev_cost: jax.Array solver_niter: jax.Array + active: jax.Array + fri: jax.Array + dm: jax.Array + u: jax.Array + h: jax.Array @classmethod def create(cls, m: Model, d: Data, grad: bool = True) -> '_Context': @@ -66,6 +76,15 @@ class _Context(PyTreeNode): # TODO(robotics-team): determine nv at which sparse mul is faster ma = support.mul_m(m, d, d.qacc) nv_0 = jp.zeros(m.nv) + fri = 0.0 + if m.opt.cone == ConeType.ELLIPTIC: + friction = d.contact.friction[d.contact.dim > 1] + dim = d.contact.dim[d.contact.dim > 1] + mu = friction[:, 0] / jp.sqrt(m.opt.impratio) + fri = jp.concatenate((mu[:, None], friction), axis=1) + for condim in (3, 4, 6): + fri = fri.at[dim == condim, condim:].set(0) + ctx = _Context( qacc=d.qacc, qfrc_constraint=d.qfrc_constraint, @@ -79,8 +98,13 @@ class _Context(PyTreeNode): cost=jp.inf, prev_cost=0.0, solver_niter=0, + active=0.0, + fri=fri, + dm=0.0, + u=0.0, + h=0.0, ) - ctx = _update_constraint(d, ctx) + ctx = _update_constraint(m, d, ctx) if grad: ctx = _update_gradient(m, d, ctx) ctx = ctx.replace(search=-ctx.Mgrad) # start with preconditioned gradient @@ -106,24 +130,68 @@ class _LSPoint(PyTreeNode): @classmethod def create( cls, + m: Model, d: Data, ctx: _Context, alpha: jax.Array, jv: jax.Array, quad: jax.Array, quad_gauss: jax.Array, + uu: jax.Array, + v0: jax.Array, + uv: jax.Array, + vv: jax.Array, ) -> '_LSPoint': """Creates a linesearch point with first and second derivatives.""" # roughly corresponds to CGEval in mujoco/src/engine/engine_solver.c # TODO(robotics-team): change this to support friction constraints - active = ((ctx.Jaref + alpha * jv) < 0).at[:d.ne + d.nf].set(True) - quad = jax.vmap(jp.multiply)(quad, active) # only active - quad_total = quad_gauss + jp.sum(quad, axis=0) + cost, deriv_0, deriv_1 = 0.0, 0.0, 0.0 + quad_total = quad_gauss + + if m.opt.cone == ConeType.ELLIPTIC: + mu, u0 = ctx.fri[:, 0], ctx.u[:, 0] + n = u0 + alpha * v0 + tsqr = uu + alpha * (2 * uv + alpha * vv) + t = jp.sqrt(tsqr) # tangential force + + bottom_zone = ((tsqr <= 0) & (n < 0)) | ((tsqr > 0) & ((mu * n + t) <= 0)) + middle_zone = (tsqr > 0) & (n < (mu * t)) & ((mu * n + t) > 0) + + # quadratic cost for equality, friction, limits, frictionless contacts + dim1 = d.contact.efc_address[d.contact.dim == 1] + nefl = d.ne + d.nf + d.nl + active = ((ctx.Jaref + alpha * jv) < 0).at[:d.ne + d.nf].set(True) + active = active.at[nefl:].set(False).at[dim1].set(active[dim1]) + quad_efld = jax.vmap(jp.multiply)(quad, active) + quad_total += jp.sum(quad_efld, axis=0) + # elliptic bottom zone: quadratic cost + efc_elliptic = d.contact.efc_address[d.contact.dim > 1] + quad_c = jax.vmap(jp.multiply)(quad[efc_elliptic], bottom_zone) + quad_total += jp.sum(quad_c, axis=0) + # elliptic middle zone + t += (t == 0) * mujoco.mjMINVAL + tsqr += (tsqr == 0) * mujoco.mjMINVAL + n1 = v0 + t1 = (uv + alpha * vv) / t + t2 = vv / t - (uv + alpha * vv) * t1 / tsqr + dm = ctx.dm * middle_zone + nmt = n - mu * t + cost = 0.5 * jp.sum(dm * jp.square(nmt)) + deriv_0 = jp.sum(dm * nmt * (n1 - mu * t1)) + deriv_1 = jp.sum(dm * (jp.square(n1 - mu * t1) - nmt * mu * t2)) + elif m.opt.cone == ConeType.PYRAMIDAL: + active = ((ctx.Jaref + alpha * jv) < 0).at[:d.ne + d.nf].set(True) + quad = jax.vmap(jp.multiply)(quad, active) # only active + quad_total += jp.sum(quad, axis=0) + else: + raise NotImplementedError(f'unsupported cone type: {m.opt.cone}') + + alpha_sq = alpha * alpha + cost += alpha_sq * quad_total[2] + alpha * quad_total[1] + quad_total[0] + deriv_0 += 2 * alpha * quad_total[2] + quad_total[1] + deriv_1 += 2 * quad_total[2] + (quad_total[2] == 0) * mujoco.mjMINVAL - cost = alpha * alpha * quad_total[2] + alpha * quad_total[1] + quad_total[0] - deriv_0 = 2 * alpha * quad_total[2] + quad_total[1] - deriv_1 = 2 * quad_total[2] + (quad_total[2] == 0) * mujoco.mjMINVAL return _LSPoint(alpha=alpha, cost=cost, deriv_0=deriv_0, deriv_1=deriv_1) @@ -159,34 +227,95 @@ def _while_loop_scan(cond_fun, body_fun, init_val, max_iter): return jax.lax.scan(_fun, init, None, length=max_iter)[0][0] -def _update_constraint(d: Data, ctx: _Context) -> _Context: +def _update_constraint(m: Model, d: Data, ctx: _Context) -> _Context: """Updates constraint force and resulting cost given latst solver iteration. Corresponds to CGupdateConstraint in mujoco/src/engine/engine_solver.c Args: + m: model defining constraints d: data which contains latest qacc and smooth terms ctx: current solver context Returns: context with new constraint force and costs """ - # TODO(robotics-team): add friction constraints + if m.opt.cone == ConeType.PYRAMIDAL: + # ne/nf constraints are always active, rest are non-negative constraints + active = (ctx.Jaref < 0).at[:d.ne + d.nf].set(True) + efc_force = d.efc_D * -ctx.Jaref * active + cost = 0.5 * jp.sum(d.efc_D * ctx.Jaref * ctx.Jaref * active) + dm, u, h = 0.0, 0.0, 0.0 + elif m.opt.cone == ConeType.ELLIPTIC: + friction = d.contact.friction[d.contact.dim > 1] + efc_address = d.contact.efc_address[d.contact.dim > 1] + dim = d.contact.dim[d.contact.dim > 1] + slice_fn = jax.vmap(lambda x: jax.lax.dynamic_slice(ctx.Jaref, (x,), (6,))) + u = slice_fn(efc_address) * ctx.fri + mu, n, t = ctx.fri[:, 0], u[:, 0], jax.vmap(math.norm)(u[:, 1:]) - # only count active constraints - active = (ctx.Jaref < 0).at[:d.ne + d.nf].set(True) + # bottom zone: quadratic + bottom_zone = ((t <= 0) & (n < 0)) | ((t > 0) & ((mu * n + t) <= 0)) + active = (ctx.Jaref < 0).at[:d.ne + d.nf].set(True) + adr_i, adr_j = [], [] + for i, (condim, addr) in enumerate(zip(dim, efc_address)): + adr_i.extend(range(addr, addr + condim)) + adr_j.extend([i] * condim) + active = active.at[jp.array(adr_i)].set(bottom_zone[jp.array(adr_j)]) + efc_force = d.efc_D * -ctx.Jaref * active + cost = 0.5 * jp.sum(d.efc_D * ctx.Jaref * ctx.Jaref * active) + + # middle zone: cone + middle_zone = (t > 0) & (n < (mu * t)) & ((mu * n + t) > 0) + dm = d.efc_D[efc_address] / jp.maximum( + mu * mu * (1 + mu * mu), mujoco.mjMINVAL + ) + nmt = n - mu * t + cost += 0.5 * jp.sum(dm * nmt * nmt * middle_zone) + # tangent and friction for middle zone: + force = -dm * nmt * mu * middle_zone + force_fri = -force / (t + ~middle_zone * mujoco.mjMINVAL) + force_fri = force_fri[:, None] * u[:, 1:] * friction + efc_force = efc_force.at[efc_address].add(force) + efc_adr, adr_i, adr_j = [], [], [] + for i, (condim, addr) in enumerate(zip(dim, efc_address)): + efc_adr.extend(range(addr + 1, addr + condim)) + adr_i.extend([i] * (condim - 1)) + adr_j.extend(range(condim - 1)) + efc_adr, adr_i, adr_j = jp.array(efc_adr), jp.array(adr_i), jp.array(adr_j) + efc_force = efc_force.at[efc_adr].add(force_fri[(adr_i, adr_j)]) + + # cone hessian + h = 0.0 + if m.opt.solver == SolverType.NEWTON: + t = jp.maximum(t, mujoco.mjMINVAL) + # h = mu*N/T^3 * U*U' + ttt = jp.maximum(t * t * t, mujoco.mjMINVAL) + h = jax.vmap(lambda x, y: x * jp.outer(y, y.T))(mu * n / ttt, u) + # add to diagonal: (mu^2 - mu*N/T) * I + h += jax.vmap(lambda x: x * jp.eye(6, 6))(mu * mu - mu * n / t) + # set first row: (1, -mu/T * U) + h_0 = jax.vmap(lambda mu, t, u: jp.append(1, -mu / t * u[1:]))(mu, t, u) + h = h.at[:, 0].set(h_0).at[:, :, 0].set(h_0) + # pre and post multiply by diag(mu, friction), scale by Dm + h *= jax.vmap(lambda d, f: d * jp.outer(f, f.T))(dm, ctx.fri) + # only cone constraints + h = jax.vmap(jp.multiply)(h, middle_zone) + else: + raise NotImplementedError(f'unsupported cone type: {m.opt.cone}') - efc_force = d.efc_D * -ctx.Jaref * active qfrc_constraint = d.efc_J.T @ efc_force gauss = 0.5 * jp.dot(ctx.Ma - d.qfrc_smooth, ctx.qacc - d.qacc_smooth) - cost = 0.5 * jp.sum(d.efc_D * ctx.Jaref * ctx.Jaref * active) + gauss - ctx = ctx.replace( qfrc_constraint=qfrc_constraint, gauss=gauss, - cost=cost, + cost=cost + gauss, prev_cost=ctx.cost, efc_force=efc_force, + active=active, + dm=dm, + u=u, + h=h, ) return ctx @@ -213,8 +342,17 @@ def _update_gradient(m: Model, d: Data, ctx: _Context) -> _Context: if m.opt.solver == SolverType.CG: mgrad = smooth.solve_m(m, d, grad) elif m.opt.solver == SolverType.NEWTON: - active = (ctx.Jaref < 0).at[: d.ne + d.nf].set(True) - h = (d.efc_J.T * d.efc_D * active) @ d.efc_J + if m.opt.cone == ConeType.ELLIPTIC: + cm = jp.diag(d.efc_D * ctx.active) + efc_address = d.contact.efc_address[d.contact.dim > 1] + dim = d.contact.dim[d.contact.dim > 1] + # set efc of cone H along diagonal + for i, (condim, addr) in enumerate(zip(dim, efc_address)): + h_cone = ctx.h[i, :condim, :condim] + cm = cm.at[addr:addr+condim, addr:addr+condim].add(h_cone) + h = d.efc_J.T @ cm @ d.efc_J + else: + h = (d.efc_J.T * d.efc_D * ctx.active) @ d.efc_J h = support.full_m(m, d) + h h_ = jax.scipy.linalg.cho_factor(h) mgrad = jax.scipy.linalg.cho_solve(h_, grad) @@ -256,8 +394,28 @@ def _linesearch(m: Model, d: Data, ctx: _Context) -> _Context: )) quad = jp.stack((0.5 * ctx.Jaref * ctx.Jaref, jv * ctx.Jaref, 0.5 * jv * jv)) quad = (quad * d.efc_D).T + uu, v0, uv, vv = 0.0, 0.0, 0.0, 0.0 + if m.opt.cone == ConeType.ELLIPTIC: + mask = d.contact.dim > 1 + # complete vector quadratic (for bottom zone) + efc_con, efc_fri = [], [] + for condim, addr in zip(d.contact.dim[mask], d.contact.efc_address[mask]): + efc_con.extend([addr] * (condim - 1)) + efc_fri.extend(range(addr + 1, addr + condim)) + quad = quad.at[jp.array(efc_con)].add(quad[jp.array(efc_fri)]) - point_fn = lambda a: _LSPoint.create(d, ctx, a, jv, quad, quad_gauss) + # rescale to make primal cone circular + jv_fn = jax.vmap(lambda x: jax.lax.dynamic_slice(jv, (x,), (6,))) + efc_elliptic = d.contact.efc_address[mask] + v = jv_fn(efc_elliptic) * ctx.fri + uu = jp.sum(ctx.u[:, 1:] * ctx.u[:, 1:], axis=1) + v0 = v[:, 0] + uv = jp.sum(ctx.u[:, 1:] * v[:, 1:], axis=1) + vv = jp.sum(v[:, 1:] * v[:, 1:], axis=1) + + point_fn = lambda a: _LSPoint.create( + m, d, ctx, a, jv, quad, quad_gauss, uu, v0, uv, vv + ) def cond(ctx: _LSContext) -> jax.Array: done = ctx.ls_iter >= m.opt.ls_iterations @@ -274,21 +432,34 @@ def _linesearch(m: Model, d: Data, ctx: _Context) -> _Context: hi_next = point_fn(hi.alpha - hi.deriv_0 / hi.deriv_1) mid = point_fn(0.5 * (lo.alpha + hi.alpha)) - # we swap lo/hi if: - # 1) they are not correctly at a bracket boundary (e.g. lo.deriv_0 > 0), OR - # 2) if moving to next or mid narrows the bracket - swap_lo_next = (lo.deriv_0 > 0) | (lo.deriv_0 < lo_next.deriv_0) - lo = jax.tree_util.tree_map(lambda x, y: jp.where(swap_lo_next, y, x), lo, lo_next) - swap_lo_mid = (mid.deriv_0 < 0) & (lo.deriv_0 < mid.deriv_0) - lo = jax.tree_util.tree_map(lambda x, y: jp.where(swap_lo_mid, y, x), lo, mid) - - swap_hi_next = (hi.deriv_0 < 0) | (hi.deriv_0 > hi_next.deriv_0) - hi = jax.tree_util.tree_map(lambda x, y: jp.where(swap_hi_next, y, x), hi, hi_next) - swap_hi_mid = (mid.deriv_0 > 0) & (hi.deriv_0 > mid.deriv_0) - hi = jax.tree_util.tree_map(lambda x, y: jp.where(swap_hi_mid, y, x), hi, mid) - - swap = swap_lo_next | swap_lo_mid | swap_hi_next | swap_hi_mid - + # swap lo/hi if the derivative points to a narrower bracket width + in_bracket = lambda x, y: ((x < y) & (y < 0) | (x > y) & (y > 0)) + swap_lo_next = in_bracket(lo.deriv_0, lo_next.deriv_0) + lo = jax.tree_util.tree_map( + lambda x, y: jp.where(swap_lo_next, y, x), lo, lo_next + ) + swap_lo_mid = in_bracket(lo.deriv_0, mid.deriv_0) + lo = jax.tree_util.tree_map( + lambda x, y: jp.where(swap_lo_mid, y, x), lo, mid + ) + swap_lo_hi_next = in_bracket(lo.deriv_0, hi_next.deriv_0) + lo = jax.tree_util.tree_map( + lambda x, y: jp.where(swap_lo_hi_next, y, x), lo, hi_next + ) + swap_hi_next = in_bracket(hi.deriv_0, hi_next.deriv_0) + hi = jax.tree_util.tree_map( + lambda x, y: jp.where(swap_hi_next, y, x), hi, hi_next + ) + swap_hi_mid = in_bracket(hi.deriv_0, mid.deriv_0) + hi = jax.tree_util.tree_map( + lambda x, y: jp.where(swap_hi_mid, y, x), hi, mid + ) + swap_hi_lo_next = in_bracket(hi.deriv_0, lo_next.deriv_0) + hi = jax.tree_util.tree_map( + lambda x, y: jp.where(swap_hi_lo_next, y, x), hi, lo_next + ) + swap = swap_lo_next | swap_lo_mid | swap_lo_hi_next + swap = swap | swap_hi_next | swap_hi_mid | swap_hi_lo_next ctx = ctx.replace(lo=lo, hi=hi, swap=swap, ls_iter=ctx.ls_iter + 1) return ctx @@ -331,14 +502,17 @@ def solve(m: Model, d: Data) -> Data: def body(ctx: _Context) -> _Context: ctx = _linesearch(m, d, ctx) prev_grad, prev_Mgrad = ctx.grad, ctx.Mgrad # pylint: disable=invalid-name - ctx = _update_constraint(d, ctx) + ctx = _update_constraint(m, d, ctx) ctx = _update_gradient(m, d, ctx) - # polak-ribiere: - beta = jp.dot(ctx.grad, ctx.Mgrad - prev_Mgrad) - beta = beta / jp.maximum(mujoco.mjMINVAL, jp.dot(prev_grad, prev_Mgrad)) - beta = jp.maximum(0, beta) - search = -ctx.Mgrad + beta * ctx.search + if m.opt.solver == SolverType.NEWTON: + search = -ctx.Mgrad + else: + # polak-ribiere: + beta = jp.dot(ctx.grad, ctx.Mgrad - prev_Mgrad) + beta = beta / jp.maximum(mujoco.mjMINVAL, jp.dot(prev_grad, prev_Mgrad)) + beta = jp.maximum(0, beta) + search = -ctx.Mgrad + beta * ctx.search ctx = ctx.replace(search=search, solver_niter=ctx.solver_niter + 1) return ctx diff --git a/mjx/mujoco/mjx/_src/solver_test.py b/mjx/mujoco/mjx/_src/solver_test.py index f4fdf732..51722940 100644 --- a/mjx/mujoco/mjx/_src/solver_test.py +++ b/mjx/mujoco/mjx/_src/solver_test.py @@ -15,16 +15,18 @@ """Tests for constraint functions.""" from absl.testing import absltest +from absl.testing import parameterized import jax import mujoco from mujoco import mjx +from mujoco.mjx._src import solver from mujoco.mjx._src import test_util import numpy as np -# tolerance for difference between MuJoCo and MJX constraint calculations, +# tolerance for difference between MuJoCo and MJX solver calculations, # mostly due to float precision -_TOLERANCE = 5e-5 +_TOLERANCE = 5e-3 def _assert_eq(a, b, name, tol=_TOLERANCE): @@ -37,72 +39,85 @@ def _assert_attr_eq(a, b, attr, tol=_TOLERANCE): _assert_eq(getattr(a, attr), getattr(b, attr), attr, tol=tol) -class SolverTest(absltest.TestCase): +class SolverTest(parameterized.TestCase): - def test_newton(self): - """Test newton solver.""" + @parameterized.parameters( + # these scene challenges the solver, with CG you need to crank up + # the iterations, otherwise it diverges + (mujoco.mjtSolver.mjSOL_CG, mujoco.mjtCone.mjCONE_PYRAMIDAL, 100), + (mujoco.mjtSolver.mjSOL_CG, mujoco.mjtCone.mjCONE_ELLIPTIC, 100), + # Newton converges much more quickly, lower iterations to demonstrate + # mgrad is being calculated optimally + (mujoco.mjtSolver.mjSOL_NEWTON, mujoco.mjtCone.mjCONE_PYRAMIDAL, 2), + (mujoco.mjtSolver.mjSOL_NEWTON, mujoco.mjtCone.mjCONE_ELLIPTIC, 2), + ) + def test_solver(self, solver_, cone, iterations): + """Test newton, CG solver with pyramidal, elliptic cones.""" m = test_util.load_test_file('constraints.xml') - # it's critical that mgrad is optimally calculated, so lower iterations - # to be sure that MJX is converging as quickly as MuJoCo - m.opt.iterations = 1 + m.opt.solver = solver_ + m.opt.cone = cone + m.opt.iterations = iterations d = mujoco.MjData(m) - mujoco.mj_step(m, d, 20) # significant constraint forces at 20 steps - # mj_forward overwrites qacc_warmstart, so let's restore it to what it was - # at the beginning of the step so that MJX does not have a trivial solution - warmstart = d.qacc_warmstart.copy() - mujoco.mj_forward(m, d) - d.qacc_warmstart = warmstart + def cost(qacc): + jaref = np.zeros(d.nefc, dtype=float) + cost = np.zeros(1) + mujoco.mj_mulJacVec(m, d, jaref, qacc) + mujoco.mj_constraintUpdate(m, d, jaref - d.efc_aref, cost, 0) + return cost - dx = jax.jit(mjx.solve)(mjx.put_model(m), mjx.put_data(m, d)) + # sample a mix of active/inactive constraints at different timesteps + for key in range(0, 3): + mujoco.mj_resetDataKeyframe(m, d, key) + mujoco.mj_step(m, d) # step to generate warmstart - _assert_attr_eq(d, dx, 'qacc') - _assert_attr_eq(d, dx, 'qfrc_constraint') - nnz = dx.efc_J.any(axis=1) - _assert_eq(d.efc_force, dx.efc_force[nnz], 'efc_force') + # compare costs + mj_cost = cost(d.qacc) + ctx = solver._Context.create(mjx.put_model(m), mjx.put_data(m, d)) + mjx_cost = ctx.cost - ctx.gauss + _assert_eq(mj_cost, mjx_cost, 'cost') - def test_cg(self): - """Test CG solver.""" - m = test_util.load_test_file('constraints.xml') - d = mujoco.MjData(m) - mujoco.mj_step(m, d, 20) # significant constraint forces at 20 steps + # mj_forward overwrites qacc_warmstart, so let's restore it to what it was + # before the step so that MJX does not have a trivial solution + warmstart = d.qacc_warmstart.copy() + mujoco.mj_forward(m, d) + d.qacc_warmstart = warmstart + dx = jax.jit(mjx.solve)(mjx.put_model(m), mjx.put_data(m, d)) - # CG does not converge as quickly as Newton but is cheaper to calculate - m.opt.solver = mujoco.mjtSolver.mjSOL_CG - m.opt.iterations = 8 + # MJX finds very similar solutions with the newton solver + if solver_ == mujoco.mjtSolver.mjSOL_NEWTON: + nnz = dx.efc_J.any(axis=1) + _assert_eq(d.efc_force, dx.efc_force[nnz], 'efc_force') + _assert_attr_eq(d, dx, 'qfrc_constraint') + _assert_attr_eq(d, dx, 'qacc') - # mj_forward overwrites qacc_warmstart, so let's restore it to what it was - # at the beginning of the step so that MJX does not have a trivial solution - warmstart = d.qacc_warmstart.copy() - mujoco.mj_forward(m, d) - d.qacc_warmstart = warmstart - - dx = jax.jit(mjx.solve)(mjx.put_model(m), mjx.put_data(m, d)) - - _assert_attr_eq(d, dx, 'qacc') - _assert_attr_eq(d, dx, 'qfrc_constraint', tol=8e-4) - nnz = dx.efc_J.any(axis=1) - _assert_eq(d.efc_force, dx.efc_force[nnz], 'efc_force', tol=5e-4) + # both CG and Newton find costs that are nearly the same as MuJoCo, often + # lower (due to slight differences in the MJX linsearch algorithm) + mj_cost = cost(d.qacc) + mjx_cost = cost(dx.qacc) + self.assertLess(mjx_cost, mj_cost * 1.01) def test_no_warmstart(self): """Test no warmstart.""" m = test_util.load_test_file('constraints.xml') d = mujoco.MjData(m) - mujoco.mj_step(m, d, 20) # significant constraint forces at 20 steps + # significant constraint forces keyframe 2 + mujoco.mj_resetDataKeyframe(m, d, 2) m.opt.disableflags |= mujoco.mjtDisableBit.mjDSBL_WARMSTART mujoco.mj_forward(m, d) mx = mjx.put_model(m) dx = jax.jit(mjx.solve)(mx, mjx.put_data(m, d)) nnz = dx.efc_J.any(axis=1) - # without warmstart, the solution is not as close - _assert_eq(d.efc_force, dx.efc_force[nnz], 'efc_force', tol=2e-2) + # even without warmstart, newton converges quickly + _assert_eq(d.efc_force, dx.efc_force[nnz], 'efc_force', tol=2e-4) def test_sparse(self): """Test solver works with sparse mass matrices.""" m = test_util.load_test_file('constraints.xml') m.opt.jacobian = mujoco.mjtJacobian.mjJAC_SPARSE d = mujoco.MjData(m) - mujoco.mj_step(m, d, 20) # significant constraint forces at 20 steps + # significant constraint forces keyframe 2 + mujoco.mj_resetDataKeyframe(m, d, 2) # mj_forward overwrites qacc_warmstart, so let's restore it to what it was # at the beginning of the step so that MJX does not have a trivial solution diff --git a/mjx/mujoco/mjx/_src/test_util.py b/mjx/mujoco/mjx/_src/test_util.py index 07a45ba5..349cab16 100644 --- a/mjx/mujoco/mjx/_src/test_util.py +++ b/mjx/mujoco/mjx/_src/test_util.py @@ -26,6 +26,7 @@ import mujoco # pylint: disable=g-importing-member from mujoco.mjx._src import forward from mujoco.mjx._src import io +from mujoco.mjx._src.types import Data # pylint: enable=g-importing-member import numpy as np @@ -104,6 +105,28 @@ def benchmark( return jit_time, run_time, steps +def efc_order(m: mujoco.MjModel, d: mujoco.MjData, dx: Data) -> np.ndarray: + """Returns a sort order such that dx.efc_*[order][:d.nefc] == d.efc_*.""" + # reorder efc rows to skip inactive constraints and match contact order + efl = dx.ne + dx.nf + dx.nl + order = np.arange(efl) + order[(dx.efc_J[:efl] == 0).all(axis=1)] = 2**16 # move empty rows to end + for i in range(dx.ncon): + num_rows = dx.contact.dim[i] + if dx.contact.dim[i] > 1 and m.opt.cone == mujoco.mjtCone.mjCONE_PYRAMIDAL: + num_rows = (dx.contact.dim[i] - 1) * 2 + if dx.contact.dist[i] > 0: # move empty contacts to end + order = np.append(order, np.repeat(2 ** 16, num_rows)) + continue + contact_match = (d.contact.geom == dx.contact.geom[i]).all(axis=-1) + contact_match &= (d.contact.pos == dx.contact.pos[i]).all(axis=-1) + assert contact_match.any(), f'contact {i} not found' + contact_id = np.nonzero(contact_match)[0][0] + order = np.append(order, np.repeat(efl + contact_id, num_rows)) + + return np.argsort(order, kind='stable') + + _ACTUATOR_TYPES = ['motor', 'velocity', 'position', 'general', 'intvelocity'] _DYN_TYPES = ['none', 'integrator', 'filter', 'filterexact'] _DYN_PRMS = ['0.189', '2.1'] diff --git a/mjx/mujoco/mjx/_src/types.py b/mjx/mujoco/mjx/_src/types.py index 0036e58d..5affb9b6 100644 --- a/mjx/mujoco/mjx/_src/types.py +++ b/mjx/mujoco/mjx/_src/types.py @@ -135,9 +135,10 @@ class ConeType(enum.IntEnum): Attributes: PYRAMIDAL: pyramidal + ELLIPTIC: elliptic """ PYRAMIDAL = mujoco.mjtCone.mjCONE_PYRAMIDAL - # unsupported: ELLIPTIC + ELLIPTIC = mujoco.mjtCone.mjCONE_ELLIPTIC class JacobianType(enum.IntEnum): @@ -245,7 +246,7 @@ class ConstraintType(enum.IntEnum): # unsupported: LIMIT_TENDON CONTACT_FRICTIONLESS = mujoco.mjtConstraint.mjCNSTR_CONTACT_FRICTIONLESS CONTACT_PYRAMIDAL = mujoco.mjtConstraint.mjCNSTR_CONTACT_PYRAMIDAL - # unsupported: CONTACT_ELLIPTIC + CONTACT_ELLIPTIC = mujoco.mjtConstraint.mjCNSTR_CONTACT_ELLIPTIC class CamLightType(enum.IntEnum): @@ -703,7 +704,7 @@ class Contact(PyTreeNode): solref: jax.Array solreffriction: jax.Array solimp: jax.Array - # unsupported: mu, H + # unsupported: mu, H (calculated locally in solver.py) dim: np.ndarray geom1: jax.Array geom2: jax.Array diff --git a/mjx/mujoco/mjx/test_data/constraints.xml b/mjx/mujoco/mjx/test_data/constraints.xml index 90c1e186..f79ee1e2 100644 --- a/mjx/mujoco/mjx/test_data/constraints.xml +++ b/mjx/mujoco/mjx/test_data/constraints.xml @@ -58,7 +58,7 @@ - + @@ -75,4 +75,13 @@ + + + + + + + + + From 7a06bcfdaf109ef09a77f80a9ebacad11662bd1f Mon Sep 17 00:00:00 2001 From: Alessio Quaglino Date: Fri, 7 Jun 2024 22:27:10 -0700 Subject: [PATCH 18/32] Make model editing API public, fixes #364 Still missing: - Detailed documentation. - Python bindings. PiperOrigin-RevId: 641445626 Change-Id: I20e67b707cf1bebae7e0cc94d17f7b76a89171f0 --- CMakeLists.txt | 1 + doc/APIreference/APItypes.rst | 429 +++- doc/APIreference/functions.rst | 952 +++++++- doc/APIreference/functions_override.rst | 9 +- doc/changelog.rst | 29 +- doc/css/theme_overrides.css | 8 + doc/includes/references.h | 737 +++++- doc/modeling.rst | 28 +- doc/overview.rst | 38 +- doc/programming/index.rst | 21 +- doc/programming/modeledit.rst | 44 + doc/programming/simulation.rst | 4 +- include/mujoco/mjspec.h | 763 ++++++ include/mujoco/mujoco.h | 371 ++- introspect/codegen/generate_structs.py | 2 +- introspect/enums.py | 63 + introspect/functions.py | 1951 ++++++++++++++- introspect/structs.py | 2991 +++++++++++++++++++++++ src/user/user_api.cc | 28 +- src/user/user_api.h | 757 +----- src/user/user_composite.cc | 2 +- src/user/user_composite.h | 2 +- src/user/user_flexcomp.cc | 6 +- src/user/user_flexcomp.h | 2 +- src/user/user_init.c | 1 + src/user/user_mesh.cc | 7 +- src/user/user_model.cc | 3 +- src/user/user_model.h | 4 +- src/user/user_objects.cc | 49 +- src/user/user_objects.h | 8 +- src/xml/xml.cc | 6 +- src/xml/xml.h | 2 +- src/xml/xml_api.cc | 14 +- src/xml/xml_api.h | 2 +- src/xml/xml_base.cc | 4 +- src/xml/xml_base.h | 2 +- src/xml/xml_native_reader.cc | 2 +- src/xml/xml_native_reader.h | 2 +- src/xml/xml_native_writer.cc | 3 +- src/xml/xml_native_writer.h | 2 +- src/xml/xml_urdf.cc | 1 + src/xml/xml_urdf.h | 2 +- test/user/user_api_test.cc | 74 +- test/xml/xml_api_test.cc | 18 +- test/xml/xml_native_reader_test.cc | 4 +- unity/Runtime/Bindings/MjBindings.cs | 40 +- 46 files changed, 8508 insertions(+), 980 deletions(-) create mode 100644 doc/programming/modeledit.rst create mode 100644 include/mujoco/mjspec.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c13da243..e1de2b0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,6 +59,7 @@ set(MUJOCO_HEADERS include/mujoco/mjmodel.h include/mujoco/mjplugin.h include/mujoco/mjrender.h + include/mujoco/mjspec.h include/mujoco/mjthread.h include/mujoco/mjtnum.h include/mujoco/mjui.h diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst index ee8de505..5ee07e6a 100644 --- a/doc/APIreference/APItypes.rst +++ b/doc/APIreference/APItypes.rst @@ -9,9 +9,11 @@ MuJoCo defines a large number of types: - Enums used in :ref:`mjModel`. - Enums used in :ref:`mjData`. - - Abstract :ref:`visualization enums`. + - Enums for abstract :ref:`visualization`. - Enums used by the :ref:`openGL renderer`. - Enums used by the :ref:`mjUI` user interface package. + - Enums used by :ref:`engine plugins`. + - Enums used for :ref:`procedural model manipulation`. Note that the API does not use these enum types directly. Instead it uses ints, and the documentation/comments state that certain ints correspond to certain enum types. This is because we want the API to be compiler-independent, and @@ -31,9 +33,10 @@ MuJoCo defines a large number of types: - Structs for :ref:`abstract visualization`. - Structs used by the :ref:`openGL renderer`. - Structs used by the :ref:`UI framework`. + - Structs used for :ref:`procedural model manipulation`. - Structs used by :ref:`engine plugins`. -- Several :ref:`tyFunction` for user-defined callbacks. +- Several :ref:`function types` for user-defined callbacks. - :ref:`tyNotes` regarding specific data structures that require detailed description. @@ -43,7 +46,7 @@ MuJoCo defines a large number of types: Primitive types --------------- -The two types below are defined in `mjtnum.h `_. +The two types below are defined in `mjtnum.h `__. .. _mjtNum: @@ -89,13 +92,14 @@ Byte type used to represent boolean variables. Enum types ---------- +All enum types use the ``mjt`` prefix. .. _tyModelEnums: Model ^^^^^ -The enums below are defined in `mjmodel.h `_. +The enums below are defined in `mjmodel.h `__. .. _mjtDisableBit: @@ -333,7 +337,7 @@ These are the possible sensor data types, used in ``mjData.sensor_datatype``. Data ^^^^ -The enums below are defined in `mjdata.h `_. +The enums below are defined in `mjdata.h `__. @@ -376,7 +380,7 @@ Timer types. The number of timer types is given by ``mjNTIMER`` which is also th Visualization ^^^^^^^^^^^^^ -The enums below are defined in `mjvisualize.h `_. +The enums below are defined in `mjvisualize.h `__. .. _mjtCatBit: @@ -480,7 +484,7 @@ These are the possible stereo rendering types. They are used in ``mjvScene.stere Rendering ^^^^^^^^^ -The enums below are defined in `mjrender.h `_. +The enums below are defined in `mjrender.h `__. .. _mjtGridPos: @@ -542,7 +546,7 @@ These are the possible font types. User Interface ^^^^^^^^^^^^^^ -The enums below are defined in `mjui.h `_. +The enums below are defined in `mjui.h `__. .. _mjtButton: @@ -574,13 +578,74 @@ Item types used in the UI framework. .. mujoco-include:: mjtItem +.. _tySpecEnums: + +Spec +^^^^ + +The enums below are defined in `mjspec.h `__. + +.. _mjtGeomInertia: + +mjtGeomInertia +~~~~~~~~~~~~~~ + +Type of inertia inference. + +.. mujoco-include:: mjtGeomInertia + +.. _mjtBuiltin: + +mjtBuiltin +~~~~~~~~~~ + +Type of built-in procedural texture. + +.. mujoco-include:: mjtBuiltin + +.. _mjtMark: + +mjtMark +~~~~~~~ + +Mark type for procedural textures. + +.. mujoco-include:: mjtMark + +.. _mjtLimited: + +mjtLimited +~~~~~~~~~~ + +Type of limit specification. + +.. mujoco-include:: mjtLimited + +.. _mjtInertiaFromGeom: + +mjtInertiaFromGeom +~~~~~~~~~~~~~~~~~~ + +Whether to infer body inertias from child geoms. + +.. mujoco-include:: mjtInertiaFromGeom + +.. _mjtOrientation: + +mjtOrientation +~~~~~~~~~~~~~~ + +Type of orientation specifier. + +.. mujoco-include:: mjtOrientation + .. _tyPluginEnums: Plugins ^^^^^^^ -The enums below are defined in `mjplugin.h `_. +The enums below are defined in `mjplugin.h `__. See :ref:`exPlugin` for details. @@ -994,6 +1059,348 @@ is initialized, others change at runtime. .. mujoco-include:: mjUI + +.. _tySpecStructure: + +mjSpec +^^^^^^ + +The strucs below are defined in `mjspec.h `__ +and, with the exception of the top level :ref:`mjSpec` struct, begin with the ``mjs`` prefix. +For more details, see the :doc:`Model Editing <../programming/modeledit>` chapter. + +.. _mjSpec: + +mjSpec +~~~~~~ + +Model specification. + +.. mujoco-include:: mjSpec + + +.. _mjsElement: + +mjsElement +~~~~~~~~~~ + +Special type corresponding to any element. + +.. mujoco-include:: mjsElement + + +.. _mjsOrientation: + +mjsOrientation +~~~~~~~~~~~~~~ + +Alternative orientation specifiers. + +.. mujoco-include:: mjsOrientation + + +.. _mjsBody: + +mjsBody +~~~~~~~ + +Body specification. + +.. mujoco-include:: mjsBody + + +.. _mjsFrame: + +mjsFrame +~~~~~~~~ + +Frame specification. + +.. mujoco-include:: mjsFrame + + +.. _mjsJoint: + +mjsJoint +~~~~~~~~ + +Joint specification. + +.. mujoco-include:: mjsJoint + + +.. _mjsGeom: + +mjsGeom +~~~~~~~ + +Geom specification. + +.. mujoco-include:: mjsGeom + + +.. _mjsSite: + +mjsSite +~~~~~~~ + +Site specification. + +.. mujoco-include:: mjsSite + + +.. _mjsCamera: + +mjsCamera +~~~~~~~~~ + +Camera specification. + +.. mujoco-include:: mjsCamera + + +.. _mjsLight: + +mjsLight +~~~~~~~~ + +Light specification. + +.. mujoco-include:: mjsLight + + +.. _mjsFlex: + +mjsFlex +~~~~~~~ + +Flex specification. + +.. mujoco-include:: mjsFlex + + +.. _mjsMesh: + +mjsMesh +~~~~~~~ + +Mesh specification. + +.. mujoco-include:: mjsMesh + + +.. _mjsHField: + +mjsHField +~~~~~~~~~ + +Height field specification. + +.. mujoco-include:: mjsHField + + +.. _mjsSkin: + +mjsSkin +~~~~~~~ + +Skin specification. + +.. mujoco-include:: mjsSkin + + +.. _mjsTexture: + +mjsTexture +~~~~~~~~~~ + +Texture specification. + +.. mujoco-include:: mjsTexture + + +.. _mjsMaterial: + +mjsMaterial +~~~~~~~~~~~ + +Material specification. + +.. mujoco-include:: mjsMaterial + + +.. _mjsPair: + +mjsPair +~~~~~~~ + +Pair specification. + +.. mujoco-include:: mjsPair + + +.. _mjsExclude: + +mjsExclude +~~~~~~~~~~ + +Exclude specification. + +.. mujoco-include:: mjsExclude + + +.. _mjsEquality: + +mjsEquality +~~~~~~~~~~~ + +Equality specification. + +.. mujoco-include:: mjsEquality + + +.. _mjsTendon: + +mjsTendon +~~~~~~~~~ + +Tendon specification. + +.. mujoco-include:: mjsTendon + + +.. _mjsWrap: + +mjsWrap +~~~~~~~ + +Wrapping object specification. + +.. mujoco-include:: mjsWrap + + +.. _mjsActuator: + +mjsActuator +~~~~~~~~~~~ + +Actuator specification. + +.. mujoco-include:: mjsActuator + + +.. _mjsSensor: + +mjsSensor +~~~~~~~~~ + +Sensor specification. + +.. mujoco-include:: mjsSensor + + +.. _mjsNumeric: + +mjsNumeric +~~~~~~~~~~ + +Custom numeric field specification. + +.. mujoco-include:: mjsNumeric + + +.. _mjsText: + +mjsText +~~~~~~~ + +Custom text specification. + +.. mujoco-include:: mjsText + + +.. _mjsTuple: + +mjsTuple +~~~~~~~~ + +Tuple specification. + +.. mujoco-include:: mjsTuple + + +.. _mjsKey: + +mjsKey +~~~~~~ + +Keyframe specification. + +.. mujoco-include:: mjsKey + + +.. _mjsDefault: + +mjsDefault +~~~~~~~~~~ + +Default specification. + +.. mujoco-include:: mjsDefault + + +.. _mjsPlugin: + +mjsPlugin +~~~~~~~~~ + +Plugin specification. + +.. mujoco-include:: mjsPlugin + + +.. _mjString: + +.. _mjStringVec: + +.. _mjIntVec: + +.. _mjIntVecVec: + +.. _mjFloatVec: + +.. _mjFloatVecVec: + +.. _mjDoubleVec: + +Array handles +~~~~~~~~~~~~~ + +Explain how handles work. + +.. code-block:: C++ + + #ifdef __cplusplus + // C++: defined to be compatible with corresponding std types + using mjString = std::string; + using mjStringVec = std::vector; + using mjIntVec = std::vector; + using mjIntVecVec = std::vector>; + using mjFloatVec = std::vector; + using mjFloatVecVec = std::vector>; + using mjDoubleVec = std::vector; + #else + // C: opaque types + typedef void mjString; + typedef void mjStringVec; + typedef void mjIntVec; + typedef void mjIntVecVec; + typedef void mjFloatVec; + typedef void mjFloatVecVec; + typedef void mjDoubleVec; + #endif + + .. _tyPluginStructure: Plugins @@ -1028,8 +1435,8 @@ Function types -------------- MuJoCo callbacks have corresponding function types. They are defined in `mjdata.h -`_ and in `mjui.h -`_. The actual callback functions are documented +`__ and in `mjui.h +`__. The actual callback functions are documented in the :doc:`globals` page. diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 7bc60256..951795ee 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -22,6 +22,42 @@ Parse XML file in MJCF or URDF format, compile it, return low-level model. If vfs is not NULL, look up files in vfs before reading from disk. If error is not NULL, it must have size error_sz. +.. _mj_parseXML: + +mj_parseXML +~~~~~~~~~~~ + +.. mujoco-include:: mj_parseXML + +Parse spec from XML file. + +.. _mj_parseXMLString: + +mj_parseXMLString +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mj_parseXMLString + +Parse spec from XML string. + +.. _mj_compile: + +mj_compile +~~~~~~~~~~ + +.. mujoco-include:: mj_compile + +Compile spec to model. + +.. _mj_recompile: + +mj_recompile +~~~~~~~~~~~~ + +.. mujoco-include:: mj_recompile + +Recompile spec to model, preserving the state. + .. _mj_saveLastXML: mj_saveLastXML @@ -41,14 +77,32 @@ mj_freeLastXML Free last XML model if loaded. Called internally at each load. -.. _mj_printSchema: +.. _mj_copyBack: -mj_printSchema -~~~~~~~~~~~~~~ +mj_copyBack +~~~~~~~~~~~ -.. mujoco-include:: mj_printSchema +.. mujoco-include:: mj_copyBack -Print internal XML schema as plain text or HTML, with style-padding or `` ``. +Copy (possibly modified) model fields back into spec. + +.. _mj_saveXMLString: + +mj_saveXMLString +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mj_saveXMLString + +Save spec to XML string, return 1 on success, 0 otherwise. + +.. _mj_saveXML: + +mj_saveXML +~~~~~~~~~~ + +.. mujoco-include:: mj_saveXML + +Save spec to XML file, return 1 on success, 0 otherwise. .. _Mainsimulation: @@ -954,8 +1008,8 @@ If ``cost`` is not ``NULL``, set ``*cost = s(jar)`` where ``jar = Jac*qacc - are .. _Raycollisions: -Ray collisions -^^^^^^^^^^^^^^ +Ray casting +^^^^^^^^^^^ Ray collisions, also known as ray casting, find the distance ``x`` of a ray's intersection with a geom, where a ray is a line emanating from the 3D point ``p`` in the direction ``v`` i.e., ``(p + x*v, x >= 0)``. All functions in this @@ -1105,6 +1159,15 @@ mju_printMatSparse Print sparse matrix to screen. +.. _mj_printSchema: + +mj_printSchema +~~~~~~~~~~~~~~ + +.. mujoco-include:: mj_printSchema + +Print internal XML schema as plain text or HTML, with style-padding or `` ``. + .. _Virtualfilesystem: Virtual file system @@ -1408,6 +1471,33 @@ mj_setLengthRange Set actuator_lengthrange for specified actuator; return 1 if ok, 0 if error. +.. _mj_makeSpec: + +mj_makeSpec +~~~~~~~~~~~ + +.. mujoco-include:: mj_makeSpec + +Create empty spec. + +.. _mj_copySpec: + +mj_copySpec +~~~~~~~~~~~ + +.. mujoco-include:: mj_copySpec + +Copy spec. + +.. _mj_deleteSpec: + +mj_deleteSpec +~~~~~~~~~~~~~ + +.. mujoco-include:: mj_deleteSpec + +Free memory allocation in mjSpec. + .. _Interaction: Interaction @@ -2106,7 +2196,7 @@ mjui_render This function is called in the screen refresh loop. It copies the offscreen OpenGL buffer to the window framebuffer. If there are multiple UIs in the application, it should be called once for each UI. Thus ``mjui_render`` is called all the -time, while :ref:`mjui_update` is called only when changes in the UI take place. +time, while :ref:`mjui_update` is called only when changes in the UI take place. dsffsdg .. _Errorandmemory: @@ -2212,6 +2302,24 @@ mju_writeLog Write [datetime, type: message] to MUJOCO_LOG.TXT. +.. _mjs_getError: + +mjs_getError +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getError + +Get compiler error message from spec. + +.. _mjs_isWarning: + +mjs_isWarning +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_isWarning + +Return 1 if compiler error is a warning. + .. _Standardmath: Standard math @@ -3359,7 +3467,7 @@ Finite-differenced discrete-time transition matrices. Letting :math:`x, u` denote the current :ref:`state` and :ref:`control` vector in an mjData instance, and letting :math:`y, s` denote the next state and sensor -values, the top-level :ref:`mj_step` function computes :math:`(x,u) \rightarrow (y,s)`. +values, the top-level :ref:`mj_step` function computes :math:`(x,u) \rightarrow (y,s)` :ref:`mjd_transitionFD` computes the four associated Jacobians using finite-differencing. These matrices and their dimensions are: @@ -3453,6 +3561,9 @@ to the inputs. Below, :math:`\bar q` denotes the pre-modified quaternion: Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math:`s = h v`). All outputs are optional. + +These functions provide high level manipulation for :ref:`mjSpec` structs, which represent an uncompiled :ref:`mjModel`. + .. _Plugins-api: Plugins @@ -3558,8 +3669,8 @@ If invalid slot number, return NULL. .. _Thread: -Thread -^^^^^^ +Threads +^^^^^^^ .. _mju_threadPoolCreate: mju_threadPoolCreate @@ -3614,3 +3725,822 @@ mju_taskJoin Wait for a task to complete. +.. _Attachment: + +Attachment +^^^^^^^^^^ +.. _mjs_attachBody: + +mjs_attachBody +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_attachBody + +Attach child body to a parent frame, return 0 on success. + +.. _mjs_attachFrame: + +mjs_attachFrame +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_attachFrame + +Attach child frame to a parent body, return 0 on success. + +.. _mjs_detachBody: + +mjs_detachBody +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_detachBody + +Detach body from mjSpec, remove all references and delete the body, return 0 on success. + +.. _AddTreeElements: + +Tree elements +^^^^^^^^^^^^^ +.. _mjs_addBody: + +mjs_addBody +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addBody + +Add child body to body, return child. + +.. _mjs_addSite: + +mjs_addSite +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addSite + +Add site to body, return site spec. + +.. _mjs_addJoint: + +mjs_addJoint +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addJoint + +Add joint to body. + +.. _mjs_addFreeJoint: + +mjs_addFreeJoint +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addFreeJoint + +Add freejoint to body. + +.. _mjs_addGeom: + +mjs_addGeom +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addGeom + +Add geom to body. + +.. _mjs_addCamera: + +mjs_addCamera +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addCamera + +Add camera to body. + +.. _mjs_addLight: + +mjs_addLight +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addLight + +Add light to body. + +.. _mjs_addFrame: + +mjs_addFrame +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addFrame + +Add frame to body. + +.. _mjs_delete: + +mjs_delete +~~~~~~~~~~ + +.. mujoco-include:: mjs_delete + +Delete object corresponding to the given element. + +.. _AddNonTreeElements: + +Non-tree elements +^^^^^^^^^^^^^^^^^ +.. _mjs_addActuator: + +mjs_addActuator +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addActuator + +Add actuator. + +.. _mjs_addSensor: + +mjs_addSensor +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addSensor + +Add sensor. + +.. _mjs_addFlex: + +mjs_addFlex +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addFlex + +Add flex. + +.. _mjs_addPair: + +mjs_addPair +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addPair + +Add contact pair. + +.. _mjs_addExclude: + +mjs_addExclude +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addExclude + +Add excluded body pair. + +.. _mjs_addEquality: + +mjs_addEquality +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addEquality + +Add equality. + +.. _mjs_addTendon: + +mjs_addTendon +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addTendon + +Add tendon. + +.. _mjs_wrapSite: + +mjs_wrapSite +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_wrapSite + +Wrap site using tendon. + +.. _mjs_wrapGeom: + +mjs_wrapGeom +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_wrapGeom + +Wrap geom using tendon. + +.. _mjs_wrapJoint: + +mjs_wrapJoint +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_wrapJoint + +Wrap joint using tendon. + +.. _mjs_wrapPulley: + +mjs_wrapPulley +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_wrapPulley + +Wrap pulley using tendon. + +.. _mjs_addNumeric: + +mjs_addNumeric +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addNumeric + +Add numeric. + +.. _mjs_addText: + +mjs_addText +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addText + +Add text. + +.. _mjs_addTuple: + +mjs_addTuple +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addTuple + +Add tuple. + +.. _mjs_addKey: + +mjs_addKey +~~~~~~~~~~ + +.. mujoco-include:: mjs_addKey + +Add keyframe. + +.. _mjs_addPlugin: + +mjs_addPlugin +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addPlugin + +Add plugin. + +.. _mjs_addDefault: + +mjs_addDefault +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addDefault + +Add default. + +.. _AddAssets: + +Assets +^^^^^^ +.. _mjs_addMesh: + +mjs_addMesh +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addMesh + +Add mesh. + +.. _mjs_addHField: + +mjs_addHField +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addHField + +Add height field. + +.. _mjs_addSkin: + +mjs_addSkin +~~~~~~~~~~~ + +.. mujoco-include:: mjs_addSkin + +Add skin. + +.. _mjs_addTexture: + +mjs_addTexture +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addTexture + +Add texture. + +.. _mjs_addMaterial: + +mjs_addMaterial +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_addMaterial + +Add material. + +.. _FindAndGetUtilities: + +Find and get utilities +^^^^^^^^^^^^^^^^^^^^^^ +.. _mjs_getSpec: + +mjs_getSpec +~~~~~~~~~~~ + +.. mujoco-include:: mjs_getSpec + +Get spec from body. + +.. _mjs_findBody: + +mjs_findBody +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_findBody + +Find body in model by name. + +.. _mjs_findChild: + +mjs_findChild +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_findChild + +Find child body by name. + +.. _mjs_findMesh: + +mjs_findMesh +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_findMesh + +Find mesh by name. + +.. _mjs_findFrame: + +mjs_findFrame +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_findFrame + +Find frame by name. + +.. _mjs_getDefault: + +mjs_getDefault +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getDefault + +Get default corresponding to an element. + +.. _mjs_findDefault: + +mjs_findDefault +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_findDefault + +Find default in model by class name. + +.. _mjs_getSpecDefault: + +mjs_getSpecDefault +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getSpecDefault + +Get global default from model. + +.. _mjs_getId: + +mjs_getId +~~~~~~~~~ + +.. mujoco-include:: mjs_getId + +Get element id. + +.. _mjs_firstChild: + +mjs_firstChild +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_firstChild + +Return body's first child of given type. + +.. _mjs_nextChild: + +mjs_nextChild +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_nextChild + +Return body's next child of the same type; return NULL if child is last. + +.. _AttributeSetters: + +Attribute setters +^^^^^^^^^^^^^^^^^ +.. _mjs_setString: + +mjs_setString +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setString + +Copy text to string. + +.. _mjs_setStringVec: + +mjs_setStringVec +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setStringVec + +Split text to entries and copy to string vector. + +.. _mjs_setInStringVec: + +mjs_setInStringVec +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setInStringVec + +Set entry in string vector. + +.. _mjs_appendString: + +mjs_appendString +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_appendString + +Append text entry to string vector. + +.. _mjs_setInt: + +mjs_setInt +~~~~~~~~~~ + +.. mujoco-include:: mjs_setInt + +Copy int array to vector. + +.. _mjs_appendIntVec: + +mjs_appendIntVec +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_appendIntVec + +Append int array to vector of arrays. + +.. _mjs_setFloat: + +mjs_setFloat +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setFloat + +Copy float array to vector. + +.. _mjs_appendFloatVec: + +mjs_appendFloatVec +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_appendFloatVec + +Append float array to vector of arrays. + +.. _mjs_setDouble: + +mjs_setDouble +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setDouble + +Copy double array to vector. + +.. _mjs_setPluginAttributes: + +mjs_setPluginAttributes +~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setPluginAttributes + +Set plugin attributes. + +.. _AttributeGetters: + +Attribute getters +^^^^^^^^^^^^^^^^^ +.. _mjs_getString: + +mjs_getString +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getString + +Get string contents. + +.. _mjs_getDouble: + +mjs_getDouble +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getDouble + +Get double array contents and optionally its size. + +.. _SpecUtilities: + +Spec utilities +^^^^^^^^^^^^^^ +.. _mjs_setActivePlugins: + +mjs_setActivePlugins +~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setActivePlugins + +Set active plugins. + +.. _mjs_setDefault: + +mjs_setDefault +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setDefault + +Set element's default. + +.. _mjs_setFrame: + +mjs_setFrame +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_setFrame + +Set element's enlcosing frame. + +.. _mjs_resolveOrientation: + +mjs_resolveOrientation +~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_resolveOrientation + +Resolve alternative orientations to quat, return error if any. + +.. _mjs_fullInertia: + +mjs_fullInertia +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_fullInertia + +Compute quat and diag inertia from full inertia matrix, return error if any. + +.. _ElementInitialization: + +Element initialization +^^^^^^^^^^^^^^^^^^^^^^ +.. _mjs_defaultSpec: + +mjs_defaultSpec +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultSpec + +Default spec attributes. + +.. _mjs_defaultOrientation: + +mjs_defaultOrientation +~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultOrientation + +Default orientation attributes. + +.. _mjs_defaultBody: + +mjs_defaultBody +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultBody + +Default body attributes. + +.. _mjs_defaultFrame: + +mjs_defaultFrame +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultFrame + +Default frame attributes. + +.. _mjs_defaultJoint: + +mjs_defaultJoint +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultJoint + +Default joint attributes. + +.. _mjs_defaultGeom: + +mjs_defaultGeom +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultGeom + +Default geom attributes. + +.. _mjs_defaultSite: + +mjs_defaultSite +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultSite + +Default site attributes. + +.. _mjs_defaultCamera: + +mjs_defaultCamera +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultCamera + +Default camera attributes. + +.. _mjs_defaultLight: + +mjs_defaultLight +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultLight + +Default light attributes. + +.. _mjs_defaultFlex: + +mjs_defaultFlex +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultFlex + +Default flex attributes. + +.. _mjs_defaultMesh: + +mjs_defaultMesh +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultMesh + +Default mesh attributes. + +.. _mjs_defaultHField: + +mjs_defaultHField +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultHField + +Default height field attributes. + +.. _mjs_defaultSkin: + +mjs_defaultSkin +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultSkin + +Default skin attributes. + +.. _mjs_defaultTexture: + +mjs_defaultTexture +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultTexture + +Default texture attributes. + +.. _mjs_defaultMaterial: + +mjs_defaultMaterial +~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultMaterial + +Default material attributes. + +.. _mjs_defaultPair: + +mjs_defaultPair +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultPair + +Default pair attributes. + +.. _mjs_defaultEquality: + +mjs_defaultEquality +~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultEquality + +Default equality attributes. + +.. _mjs_defaultTendon: + +mjs_defaultTendon +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultTendon + +Default tendon attributes. + +.. _mjs_defaultActuator: + +mjs_defaultActuator +~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultActuator + +Default actuator attributes. + +.. _mjs_defaultSensor: + +mjs_defaultSensor +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultSensor + +Default sensor attributes. + +.. _mjs_defaultNumeric: + +mjs_defaultNumeric +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultNumeric + +Default numeric attributes. + +.. _mjs_defaultText: + +mjs_defaultText +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultText + +Default text attributes. + +.. _mjs_defaultTuple: + +mjs_defaultTuple +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultTuple + +Default tuple attributes. + +.. _mjs_defaultKey: + +mjs_defaultKey +~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultKey + +Default keyframe attributes. + +.. _mjs_defaultPlugin: + +mjs_defaultPlugin +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_defaultPlugin + +Default plugin attributes. + diff --git a/doc/APIreference/functions_override.rst b/doc/APIreference/functions_override.rst index 9f39042d..cb212686 100644 --- a/doc/APIreference/functions_override.rst +++ b/doc/APIreference/functions_override.rst @@ -312,7 +312,7 @@ depending on which UI item was modified and what the state of that item is after This function is called in the screen refresh loop. It copies the offscreen OpenGL buffer to the window framebuffer. If there are multiple UIs in the application, it should be called once for each UI. Thus ``mjui_render`` is called all the -time, while :ref:`mjui_update` is called only when changes in the UI take place. +time, while :ref:`mjui_update` is called only when changes in the UI take place. dsffsdg @@ -545,7 +545,7 @@ Finite-differenced discrete-time transition matrices. Letting :math:`x, u` denote the current :ref:`state` and :ref:`control` vector in an mjData instance, and letting :math:`y, s` denote the next state and sensor -values, the top-level :ref:`mj_step` function computes :math:`(x,u) \rightarrow (y,s)`. +values, the top-level :ref:`mj_step` function computes :math:`(x,u) \rightarrow (y,s)` :ref:`mjd_transitionFD` computes the four associated Jacobians using finite-differencing. These matrices and their dimensions are: @@ -623,3 +623,8 @@ to the inputs. Below, :math:`\bar q` denotes the pre-modified quaternion: Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math:`s = h v`). All outputs are optional. + +.. _SpecManip: + +These functions provide high level manipulation for :ref:`mjSpec` structs, which represent an uncompiled :ref:`mjModel`. + diff --git a/doc/changelog.rst b/doc/changelog.rst index 2e42197a..c265aa27 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -7,16 +7,21 @@ Upcoming version (not yet released) General ^^^^^^^ +1. Added a new API for :doc:`procedural model manipulation`. Fixes :github:issue:`364`. + Still missing: + + - Detailed documentation. + - Python bindings. + +2. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. -1. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. MJX ~~~ -2. Added support for :ref:`elliptic friction cones`. -3. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. -4. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. - +3. Added support for :ref:`elliptic friction cones`. +4. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. +5. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. Version 3.1.6 (Jun 3, 2024) --------------------------- @@ -731,13 +736,13 @@ General Previously, the smooth part consisted of two stitched quadratics, once continuously differentiable. It is now a single quintic, twice continuously differentiable: - .. math:: - s(x) = - \begin{cases} - 0, & & x \le 0 \\ - 6x^5 - 15x^4 + 10x^3, & 0 \lt & x \lt 1 \\ - 1, & 1 \le & x \qquad - \end{cases} + .. math:: + s(x) = + \begin{cases} + 0, & & x \le 0 \\ + 6x^5 - 15x^4 + 10x^3, & 0 \lt & x \lt 1 \\ + 1, & 1 \le & x \qquad + \end{cases} 17. Added optional :ref:`tausmooth` attribute to muscle actuators. When positive, the time-constant :math:`\tau` of muscle activation/deactivation uses :ref:`mju_sigmoid` to transition smoothly diff --git a/doc/css/theme_overrides.css b/doc/css/theme_overrides.css index 6b607bda..e51e4eb0 100644 --- a/doc/css/theme_overrides.css +++ b/doc/css/theme_overrides.css @@ -266,6 +266,14 @@ dt .at { margin-bottom: 0.3em; } +/* Adjust margins around code blocks */ +.highlight pre { + margin-top: -0.3em; + margin-bottom: -0.3em; + margin-left: -0.5em; + margin-right: -0.5em; +} + details summary { font-weight: 600; } diff --git a/doc/includes/references.h b/doc/includes/references.h index c85c07eb..253ced07 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -1598,6 +1598,637 @@ struct mjrContext_ { // custom OpenGL context int readDepthMap; // depth mapping: mjDEPTH_ZERONEAR or mjDEPTH_ZEROFAR }; typedef struct mjrContext_ mjrContext; +typedef enum mjtGeomInertia_ { // type of inertia inference + mjINERTIA_VOLUME, // mass distributed in the volume + mjINERTIA_SHELL, // mass distributed on the surface +} mjtGeomInertia; +typedef enum mjtBuiltin_ { // type of built-in procedural texture + mjBUILTIN_NONE = 0, // no built-in texture + mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2 + mjBUILTIN_CHECKER, // checker pattern: rgb1, rgb2 + mjBUILTIN_FLAT // 2d: rgb1; cube: rgb1-up, rgb2-side, rgb3-down +} mjtBuiltin; +typedef enum mjtMark_ { // mark type for procedural textures + mjMARK_NONE = 0, // no mark + mjMARK_EDGE, // edges + mjMARK_CROSS, // cross + mjMARK_RANDOM // random dots +} mjtMark; +typedef enum mjtLimited_ { // type of limit specification + mjLIMITED_FALSE = 0, // not limited + mjLIMITED_TRUE, // limited + mjLIMITED_AUTO, // limited inferred from presence of range +} mjtLimited; +typedef enum mjtInertiaFromGeom_ { // whether to infer body inertias from child geoms + mjINERTIAFROMGEOM_FALSE = 0, // do not use; inertial element required + mjINERTIAFROMGEOM_TRUE, // always use; overwrite inertial element + mjINERTIAFROMGEOM_AUTO // use only if inertial element is missing +} mjtInertiaFromGeom; +typedef enum mjtOrientation_ { // type of orientation specifier + mjORIENTATION_QUAT = 0, // quaternion + mjORIENTATION_AXISANGLE, // axis and angle + mjORIENTATION_XYAXES, // x and y axes + mjORIENTATION_ZAXIS, // z axis (minimal rotation) + mjORIENTATION_EULER, // Euler angles +} mjtOrientation; +typedef struct mjsElement_ { // element type, do not modify + mjtObj elemtype; // element type +} mjsElement; +typedef struct mjSpec_ { // model specification + mjsElement* element; // element type + mjString* modelname; // model name + + // compiler settings + mjtByte autolimits; // infer "limited" attribute based on range + double boundmass; // enforce minimum body mass + double boundinertia; // enforce minimum body diagonal inertia + double settotalmass; // rescale masses and inertias; <=0: ignore + mjtByte balanceinertia; // automatically impose A + B >= C rule + mjtByte strippath; // automatically strip paths from mesh files + mjtByte fitaabb; // meshfit to aabb instead of inertia box + mjtByte degree; // angles in radians or degrees + char euler[3]; // sequence for euler rotations + mjString* meshdir; // mesh and hfield directory + mjString* texturedir; // texture directory + mjtByte discardvisual; // discard visual geoms in parser + mjtByte convexhull; // compute mesh convex hulls + mjtByte usethread; // use multiple threads to speed up compiler + mjtByte fusestatic; // fuse static bodies with parent + int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom) + int inertiagrouprange[2]; // range of geom groups used to compute inertia + mjtByte exactmeshinertia; // if false, use old formula + mjLROpt LRopt; // options for lengthrange computation + + // engine data + mjOption option; // physics options + mjVisual visual; // visual options + mjStatistic stat; // statistics override (if defined) + + // sizes + size_t memory; // number of bytes in arena+stack memory + int nemax; // max number of equality constraints + int nuserdata; // number of mjtNums in userdata + int nuser_body; // number of mjtNums in body_user + int nuser_jnt; // number of mjtNums in jnt_user + int nuser_geom; // number of mjtNums in geom_user + int nuser_site; // number of mjtNums in site_user + int nuser_cam; // number of mjtNums in cam_user + int nuser_tendon; // number of mjtNums in tendon_user + int nuser_actuator; // number of mjtNums in actuator_user + int nuser_sensor; // number of mjtNums in sensor_user + int nkey; // number of keyframes + int njmax; // (deprecated) max number of constraints + int nconmax; // (deprecated) max number of detected contacts + size_t nstack; // (deprecated) number of mjtNums in mjData stack + + // global data + mjString* comment; // comment at top of XML + mjString* modelfiledir; // path to model file + + // other + mjtByte hasImplicitPluginElem; // already encountered an implicit plugin sensor/actuator +} mjSpec; +typedef struct mjsOrientation_ { // alternative orientation specifiers + mjtOrientation type; // active orientation specifier + double axisangle[4]; // axis and angle + double xyaxes[6]; // x and y axes + double zaxis[3]; // z axis (minimal rotation) + double euler[3]; // Euler angles +} mjsOrientation; +typedef struct mjsPlugin_ { // plugin specification + mjsElement* instance; // element type + mjString* name; // name + mjString* instance_name; // instance name + int plugin_slot; // global registered slot number of the plugin + mjtByte active; // is the plugin active + mjString* info; // message appended to compiler errors +} mjsPlugin; +typedef struct mjsBody_ { // body specification + mjsElement* element; // element type + mjString* name; // name + mjString* childclass; // childclass name + + // body frame + double pos[3]; // frame position + double quat[4]; // frame orientation + mjsOrientation alt; // frame alternative orientation + + // inertial frame + double mass; // mass + double ipos[3]; // inertial frame position + double iquat[4]; // inertial frame orientation + double inertia[3]; // diagonal inertia (in i-frame) + mjsOrientation ialt; // inertial frame alternative orientation + double fullinertia[6]; // non-axis-aligned inertia matrix + + // other + mjtByte mocap; // is this a mocap body + double gravcomp; // gravity compensation + mjDoubleVec* userdata; // user data + mjtByte explicitinertial; // whether to save the body with explicit inertial clause + mjsPlugin plugin; // passive force plugin + mjString* info; // message appended to compiler errors +} mjsBody; +typedef struct mjsFrame_ { // frame specification + mjsElement* element; // element type + mjString* name; // name + mjString* childclass; // childclass name + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + mjString* info; // message appended to compiler errors +} mjsFrame; +typedef struct mjsJoint_ { // joint specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + mjtJoint type; // joint type + + // kinematics + double pos[3]; // anchor position + double axis[3]; // joint axis + double ref; // value at reference configuration: qpos0 + + // stiffness + double stiffness; // stiffness coefficient + double springref; // spring reference value: qpos_spring + double springdamper[2]; // timeconst, dampratio + + // limits + int limited; // does joint have limits (mjtLimited) + double range[2]; // joint limits + double margin; // margin value for joint limit detection + mjtNum solref_limit[mjNREF]; // solver reference: joint limits + mjtNum solimp_limit[mjNIMP]; // solver impedance: joint limits + int actfrclimited; // are actuator forces on joint limited (mjtLimited) + double actfrcrange[2]; // actuator force limits + + // dof properties + double armature; // armature inertia (mass for slider) + double damping; // damping coefficient + double frictionloss; // friction loss + mjtNum solref_friction[mjNREF]; // solver reference: dof friction + mjtNum solimp_friction[mjNIMP]; // solver impedance: dof friction + + // other + int group; // group + mjtByte actgravcomp; // is gravcomp force applied via actuators + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors +} mjsJoint; +typedef struct mjsGeom_ { // geom specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // classname + mjtGeom type; // geom type + + // frame, size + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + double fromto[6]; // alternative for capsule, cylinder, box, ellipsoid + double size[3]; // type-specific size + + // contact related + int contype; // contact type + int conaffinity; // contact affinity + int condim; // contact dimensionality + int priority; // contact priority + double friction[3]; // one-sided friction coefficients: slide, roll, spin + double solmix; // solver mixing for contact pairs + mjtNum solref[mjNREF]; // solver reference + mjtNum solimp[mjNIMP]; // solver impedance + double margin; // margin for contact detection + double gap; // include in solver if dist < margin-gap + + // inertia inference + double mass; // used to compute density + double density; // used to compute mass and inertia from volume or surface + mjtGeomInertia typeinertia; // selects between surface and volume inertia + + // fluid forces + mjtNum fluid_ellipsoid; // whether ellipsoid-fluid model is active + mjtNum fluid_coefs[5]; // ellipsoid-fluid interaction coefs + + // visual + mjString* material; // name of material + float rgba[4]; // rgba when material is omitted + int group; // group + + // other + mjString* hfieldname; // heightfield attached to geom + mjString* meshname; // mesh attached to geom + double fitscale; // scale mesh uniformly + mjDoubleVec* userdata; // user data + mjsPlugin plugin; // sdf plugin + mjString* info; // message appended to compiler errors +} mjsGeom; +typedef struct mjsSite_ { // site specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // frame, size + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + double fromto[6]; // alternative for capsule, cylinder, box, ellipsoid + double size[3]; // geom size + + // visual + mjtGeom type; // geom type + mjString* material; // name of material + int group; // group + float rgba[4]; // rgba when material is omitted + + // other + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors +} mjsSite; +typedef struct mjsCamera_ { // camera specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // extrinsics + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + mjtCamLight mode; // tracking mode + mjString* targetbody; // target body for tracking/targeting + + // intrinsics + double fovy; // y-field of view + double ipd; // inter-pupilary distance + float intrinsic[4]; // camera intrinsics (length) + float sensor_size[2]; // sensor size (length) + float resolution[2]; // resolution (pixel) + float focal_length[2]; // focal length (length) + float focal_pixel[2]; // focal length (pixel) + float principal_length[2]; // principal point (length) + float principal_pixel[2]; // principal point (pixel) + + // other + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors +} mjsCamera; +typedef struct mjsLight_ { // light specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // frame + double pos[3]; // position + double dir[3]; // direction + mjtCamLight mode; // tracking mode + mjString* targetbody; // target body for targeting + + // intrinsics + mjtByte active; // is light active + mjtByte directional; // is light directional or spot + mjtByte castshadow; // does light cast shadows + double bulbradius; // bulb radius, for soft shadows + float attenuation[3]; // OpenGL attenuation (quadratic model) + float cutoff; // OpenGL cutoff + float exponent; // OpenGL exponent + float ambient[3]; // ambient color + float diffuse[3]; // diffuse color + float specular[3]; // specular color + + // other + mjString* info; // message appended to compiler errorsx +} mjsLight; +typedef struct mjsFlex_ { // flex specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // contact properties + int contype; // contact type + int conaffinity; // contact affinity + int condim; // contact dimensionality + int priority; // contact priority + double friction[3]; // one-sided friction coefficients: slide, roll, spin + double solmix; // solver mixing for contact pairs + mjtNum solref[mjNREF]; // solver reference + mjtNum solimp[mjNIMP]; // solver impedance + double margin; // margin for contact detection + double gap; // include in solver if dist`---it is compiled into :ref:`mjModel`. +Compilation is independent of loading, meaning that the compiler works in the same way regardless of how :ref:`mjSpec` +was created. Both the parser and the compiler perform extensive error checking, and abort when the first error is encountered. The resulting error messages contain the row and column number in the XML file, and are self-explanatory so we do not document them here. The parser uses a custom schema to make sure that the file structure, elements and attributes are valid. The compiler then applies many additional semantic checks. Finally, one @@ -72,9 +72,9 @@ binary MJB file with :ref:`mj_saveModel`. The MJB is a stand-alone file and does refer to any other files. It also loads faster. So we recommend saving commonly used models as MJB and loading them when needed for simulation. -It is also possible to save a compiled mjCModel as MJCF with :ref:`mj_saveLastXML`. If any real-valued fields in the -corresponding mjModel were modified after compilation (which is unusual but can happen in system identification -applications for example), the modifications are automatically copied back into mjCModel before saving. Note that +It is also possible to save a compiled :ref:`mjSpec` as MJCF with :ref:`mj_saveLastXML`. If any real-valued fields in +the corresponding mjModel were modified after compilation (which is unusual but can happen in system identification +applications for example), the modifications are automatically copied back into :ref:`mjSpec` before saving. Note that structural changes cannot be made in the compiled model. The XML writer attempts to generate the smallest MJCF file which is guaranteed to compile into the same model, modulo negligible numeric differences caused by the plain text representation of real values. The resulting file may not have the same structure as the original because MJCF has many @@ -83,6 +83,14 @@ subset of MJCF where all coordinates are local and all body positions, orientati explicitly specified. In the Computation chapter we showed an `example <_static/example.xml>`__ MJCF file and the corresponding `saved example <_static/example_saved.xml>`__. +.. _EditModel: + +Editing models +~~~~~~~~~~~~~~ + +As of MuJoCo 3.2, it is possible to create and modify models using the :ref:`mjSpec` struct and related API. +For further documentation, please see the :doc:`Model Editing` chapter. + .. _Mechanisms: MJCF Mechanisms diff --git a/doc/overview.rst b/doc/overview.rst index fff37970..068a66e5 100644 --- a/doc/overview.rst +++ b/doc/overview.rst @@ -141,32 +141,30 @@ There are several entities called "model" in MuJoCo. The user defines the model The software can then create multiple instances of the same model in different media (file or memory) and on different levels of description (high or low). All combinations are possible as shown in the following table: -+------------+----------------------+----------------------+ -| | High level | Low level | -+============+======================+======================+ -| **File** | MJCF/URDF (XML) | MJB (binary) | -+------------+----------------------+----------------------+ -| **Memory** | mjCModel (C++ class) | mjModel (C struct) | -+------------+----------------------+----------------------+ ++------------+---------------------------+----------------------------+ +| | High level | Low level | ++============+===========================+============================+ +| **File** | MJCF/URDF (XML) | MJB (binary) | ++------------+---------------------------+----------------------------+ +| **Memory** | :ref:`mjSpec` (C struct) | :ref:`mjModel` (C struct) | ++------------+---------------------------+----------------------------+ -All runtime computations are performed with ``mjModel`` which is too complex to create manually. This is why we have two -levels of modeling. The high level exists for user convenience: its sole purpose is to be compiled into a low level -model on which computations can be performed. The resulting ``mjModel`` can be loaded and saved into a binary file +All runtime computations are performed with :ref:`mjModel` which is too complex to create manually. This is why we have +two levels of modeling. The high level exists for user convenience: its sole purpose is to be compiled into a low level +model on which computations can be performed. The resulting :ref:`mjModel` can be loaded and saved into a binary file (MJB), however those are version-specific and cannot be decompiled, thus models should always be maintained as XML files. -The (internal) C++ class ``mjCModel`` is roughly in one-to-one correspondence with the MJCF file format. The XML parser -interprets the MJCF or URDF file and creates the corresponding ``mjCModel``. In principle the user can create -``mjCModel`` programmatically and then save it to MJCF or compile it. However this functionality is not yet exposed -because a C++ API cannot be exported from a compiler-independent library. There is a plan to develop a C wrapper around -it, but for the time being the parser and compiler are always invoked together, and models can only be created in XML. +The :ref:`mjSpec` C struct is in one-to-one correspondence with the MJCF file format. The XML loader interprets the MJCF +or URDF file, creates the corresponding :ref:`mjSpec` and compiles it to :ref:`mjModel`. The user can create +:ref:`mjSpec` programmatically and then save it to MJCF or compile it. Procedural model creation and editing is +described in the :doc:`Model Editing ` chapter. -The following diagram shows the different paths to obtaining an ``mjModel`` (again, the second bullet point is not yet -available): +The following diagram shows the different paths to obtaining an :ref:`mjModel`: -- (text editor) → MJCF/URDF file → (MuJoCo parser → mjCModel → MuJoCo compiler) → mjModel -- (user code) → mjCModel → (MuJoCo compiler) → mjModel -- MJB file → (MuJoCo loader) → mjModel +- (text editor) → MJCF/URDF file → (MuJoCo parser → mjSpec → compiler) → mjModel +- (user code) → mjSpec → (MuJoCo compiler) → mjModel +- MJB file → (model loader) → mjModel .. _Examples: diff --git a/doc/programming/index.rst b/doc/programming/index.rst index 11273f46..5d438482 100644 --- a/doc/programming/index.rst +++ b/doc/programming/index.rst @@ -18,7 +18,7 @@ Engine The simulator (or physics engine) is written in C. It is responsible for all runtime computations. Parser The XML parser is written in C++. It can parse MJCF models and URDF models, converting them into an internal mjCModel - C++ object which is not directly exposed to the user. + C++ object which is exposed to the user via mjSpec. Compiler The compiler is written in C++. It takes an mjCModel C++ object constructed by the parser, and converts it into an mjModel C structure used at runtime. @@ -108,10 +108,10 @@ Building from source To build MuJoCo from source, you will need CMake and a working C++17 compiler installed. The steps are: - #. Clone the ``mujoco`` repository from GitHub. - #. Create a new build directory somewhere, and ``cd`` into it. - #. Run ``cmake $PATH_TO_CLONED_REPO`` to configure the build. - #. Run ``cmake --build .`` to build. +#. Clone the ``mujoco`` repository from GitHub. +#. Create a new build directory somewhere, and ``cd`` into it. +#. Run ``cmake $PATH_TO_CLONED_REPO`` to configure the build. +#. Run ``cmake --build .`` to build. MuJoCo's build system automatically fetches dependencies from upstream repositories over the Internet using CMake's `FetchContent `_ module. @@ -123,8 +123,8 @@ section of the documentation. Additionally, the CMake setup also implements an installation phase which will copy and organize the output files to a target directory. - 5. Select the directory: ``cmake $PATH_TO_CLONED_REPO -DCMAKE_INSTALL_PREFIX=`` - #. After building, install with ``cmake --install .`` +5. Select the directory: ``cmake $PATH_TO_CLONED_REPO -DCMAKE_INSTALL_PREFIX=`` +#. After building, install with ``cmake --install .`` When building on Windows, use Visual Studio 2019 or later and make sure Windows SDK version 10.0.22000 or later is installed (see `here `__ for more details). @@ -160,6 +160,8 @@ links below, to make this documentation self-contained. Defines the primitive types and structures needed by the UI framework. `mjtnum.h `__ Defines MuJoCo's ``mjtNum`` floating-point type to be either ``double`` or ``float``. See :ref:`mjtNum`. +`mjspec.h `__ + Defines enums and structs used for :doc:`procedural model editing `. `mjmacro.h `__ Defines C macros that are useful in user code. `mjxmacro.h `__ @@ -226,6 +228,8 @@ to which the symbol belongs. First we list the prefixes corresponding to type de Data structure related to OpenGL rendering, for example :ref:`mjrContext`. ``mjui`` Data structure related to UI framework, for example :ref:`mjuiSection`. +``mjs`` + Data structure related :doc:`procedural model editing `, for example :ref:`mjsJoint`. Next we list the prefixes corresponding to function definitions. Note that function prefixes always end with underscore. @@ -247,6 +251,8 @@ Next we list the prefixes corresponding to function definitions. Note that funct custom callbacks by setting these global pointers to user-defined functions. ``mjd_`` Functions for computing derivatives, for example :ref:`mjd_transitionFD`. +``mjs_`` + Functions for :doc:`procedural model editing `, for example :ref:`mjs_addJoint`. .. _inOpenGL: @@ -276,5 +282,6 @@ now lazily resolved at runtime after the switch to GLAD, the "nogl" libraries ar simulation visualization ui + modeledit samples extension diff --git a/doc/programming/modeledit.rst b/doc/programming/modeledit.rst new file mode 100644 index 00000000..33181d1d --- /dev/null +++ b/doc/programming/modeledit.rst @@ -0,0 +1,44 @@ +Model Editing +------------- + +.. admonition:: Unstable API + :class: attention + + The API described below is new and unstable. There may be latent bugs and function signatures may change. Early + adopters are welcome (indeed, encouraged) to try it out and report any issues on GitHub. + +As of MuJoCo 3.2, it is possible to create and modify models using the :ref:`mjSpec` struct and related API. +This datastructure is in one-to-one correspondence with MJCF and indeed, MuJoCo's own XML parsers (both MJCF and URDF) +use this API when loading a model. + + +.. _meOverview: + +Overview +~~~~~~~~ + +As summarized in the the :ref:`Overview chapter`, the traditional workflow to create compiled :ref:`mjModel` +instances is: + +1. Create an XML model description file (MJCF or URDF). +2. Call :ref:`mj_loadXML` passing in the XML (and associated assets), obtain an :ref:`mjModel` instance. + +The new workflow looks like: + +1. Create an :ref:`mjSpec`, either an empty one corresponding to the XML ````, or by loading an existing XML + file. +2. Modify the :ref:`mjSpec` as desired, adding, editing and removing elements. +3. Compile the :ref:`mjSpec` at any point, obtaining an updated :ref:`mjModel` instance. After compilation, the + :ref:`mjSpec` remains editable, so steps 2 and 3 are interchangable. + + +.. _meUsage: + +Usage +~~~~~ + +Detailed documentation is still missing. In the meantime, advanced users can refer to +`user_api_test.cc `__ and the MJCF +parser in `xml_native_reader.cc `__, +which is already using this API. + diff --git a/doc/programming/simulation.rst b/doc/programming/simulation.rst index 5b3d50f8..6939a421 100644 --- a/doc/programming/simulation.rst +++ b/doc/programming/simulation.rst @@ -585,8 +585,8 @@ corresponding to precomputed quantities when the model is in the reference confi Finally, if changes are made to mjModel at runtime, it may be desirable to save them back to the XML. The function :ref:`mj_saveLastXML` does that in a limited sense: it copies all real-valued parameters from mjModel back to the -internal mjCModel, and then saves it as XML. This does not cover all possible changes that the user could have made. -The only way to guarantee that all changes are saved is to save the model as a binary MJB file with the function +internal :ref:`mjSpec`, and then saves it as XML. This does not cover all possible changes that the user could have +made. The only way to guarantee that all changes are saved is to save the model as a binary MJB file with the function :ref:`mj_saveModel`, or even better, make the changes directly in the XML. Unfortunately there are situations where changes need to be made programmatically, as in system identification for example, and this can only be done with the compiled model. So in summary, we have reasonable but not perfect mechanisms for saving model changes. The reason for diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h new file mode 100644 index 00000000..a52f7d7d --- /dev/null +++ b/include/mujoco/mjspec.h @@ -0,0 +1,763 @@ +// Copyright 2024 DeepMind Technologies Limited +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef MUJOCO_INCLUDE_MJSPEC_H_ +#define MUJOCO_INCLUDE_MJSPEC_H_ + +#include +#include +#include + + +// this is a C-API +#ifdef __cplusplus +#include +#include + +extern "C" { +#endif + +//-------------------------------- handles to strings and arrays ----------------------------------- + +#ifdef __cplusplus + // C++: defined to be compatible with corresponding std types + using mjString = std::string; + using mjStringVec = std::vector; + using mjIntVec = std::vector; + using mjIntVecVec = std::vector>; + using mjFloatVec = std::vector; + using mjFloatVecVec = std::vector>; + using mjDoubleVec = std::vector; +#else + // C: opaque types + typedef void mjString; + typedef void mjStringVec; + typedef void mjIntVec; + typedef void mjIntVecVec; + typedef void mjFloatVec; + typedef void mjFloatVecVec; + typedef void mjDoubleVec; +#endif + + +//-------------------------------- enum types (mjt) ------------------------------------------------ + +typedef enum mjtGeomInertia_ { // type of inertia inference + mjINERTIA_VOLUME, // mass distributed in the volume + mjINERTIA_SHELL, // mass distributed on the surface +} mjtGeomInertia; + + +typedef enum mjtBuiltin_ { // type of built-in procedural texture + mjBUILTIN_NONE = 0, // no built-in texture + mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2 + mjBUILTIN_CHECKER, // checker pattern: rgb1, rgb2 + mjBUILTIN_FLAT // 2d: rgb1; cube: rgb1-up, rgb2-side, rgb3-down +} mjtBuiltin; + + +typedef enum mjtMark_ { // mark type for procedural textures + mjMARK_NONE = 0, // no mark + mjMARK_EDGE, // edges + mjMARK_CROSS, // cross + mjMARK_RANDOM // random dots +} mjtMark; + + +typedef enum mjtLimited_ { // type of limit specification + mjLIMITED_FALSE = 0, // not limited + mjLIMITED_TRUE, // limited + mjLIMITED_AUTO, // limited inferred from presence of range +} mjtLimited; + + +typedef enum mjtInertiaFromGeom_ { // whether to infer body inertias from child geoms + mjINERTIAFROMGEOM_FALSE = 0, // do not use; inertial element required + mjINERTIAFROMGEOM_TRUE, // always use; overwrite inertial element + mjINERTIAFROMGEOM_AUTO // use only if inertial element is missing +} mjtInertiaFromGeom; + + +typedef enum mjtOrientation_ { // type of orientation specifier + mjORIENTATION_QUAT = 0, // quaternion + mjORIENTATION_AXISANGLE, // axis and angle + mjORIENTATION_XYAXES, // x and y axes + mjORIENTATION_ZAXIS, // z axis (minimal rotation) + mjORIENTATION_EULER, // Euler angles +} mjtOrientation; + + +//-------------------------------- attribute structs (mjs) ----------------------------------------- + +typedef struct mjsElement_ { // element type, do not modify + mjtObj elemtype; // element type +} mjsElement; + + +typedef struct mjSpec_ { // model specification + mjsElement* element; // element type + mjString* modelname; // model name + + // compiler settings + mjtByte autolimits; // infer "limited" attribute based on range + double boundmass; // enforce minimum body mass + double boundinertia; // enforce minimum body diagonal inertia + double settotalmass; // rescale masses and inertias; <=0: ignore + mjtByte balanceinertia; // automatically impose A + B >= C rule + mjtByte strippath; // automatically strip paths from mesh files + mjtByte fitaabb; // meshfit to aabb instead of inertia box + mjtByte degree; // angles in radians or degrees + char euler[3]; // sequence for euler rotations + mjString* meshdir; // mesh and hfield directory + mjString* texturedir; // texture directory + mjtByte discardvisual; // discard visual geoms in parser + mjtByte convexhull; // compute mesh convex hulls + mjtByte usethread; // use multiple threads to speed up compiler + mjtByte fusestatic; // fuse static bodies with parent + int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom) + int inertiagrouprange[2]; // range of geom groups used to compute inertia + mjtByte exactmeshinertia; // if false, use old formula + mjLROpt LRopt; // options for lengthrange computation + + // engine data + mjOption option; // physics options + mjVisual visual; // visual options + mjStatistic stat; // statistics override (if defined) + + // sizes + size_t memory; // number of bytes in arena+stack memory + int nemax; // max number of equality constraints + int nuserdata; // number of mjtNums in userdata + int nuser_body; // number of mjtNums in body_user + int nuser_jnt; // number of mjtNums in jnt_user + int nuser_geom; // number of mjtNums in geom_user + int nuser_site; // number of mjtNums in site_user + int nuser_cam; // number of mjtNums in cam_user + int nuser_tendon; // number of mjtNums in tendon_user + int nuser_actuator; // number of mjtNums in actuator_user + int nuser_sensor; // number of mjtNums in sensor_user + int nkey; // number of keyframes + int njmax; // (deprecated) max number of constraints + int nconmax; // (deprecated) max number of detected contacts + size_t nstack; // (deprecated) number of mjtNums in mjData stack + + // global data + mjString* comment; // comment at top of XML + mjString* modelfiledir; // path to model file + + // other + mjtByte hasImplicitPluginElem; // already encountered an implicit plugin sensor/actuator +} mjSpec; + + +typedef struct mjsOrientation_ { // alternative orientation specifiers + mjtOrientation type; // active orientation specifier + double axisangle[4]; // axis and angle + double xyaxes[6]; // x and y axes + double zaxis[3]; // z axis (minimal rotation) + double euler[3]; // Euler angles +} mjsOrientation; + + +typedef struct mjsPlugin_ { // plugin specification + mjsElement* instance; // element type + mjString* name; // name + mjString* instance_name; // instance name + int plugin_slot; // global registered slot number of the plugin + mjtByte active; // is the plugin active + mjString* info; // message appended to compiler errors +} mjsPlugin; + + +typedef struct mjsBody_ { // body specification + mjsElement* element; // element type + mjString* name; // name + mjString* childclass; // childclass name + + // body frame + double pos[3]; // frame position + double quat[4]; // frame orientation + mjsOrientation alt; // frame alternative orientation + + // inertial frame + double mass; // mass + double ipos[3]; // inertial frame position + double iquat[4]; // inertial frame orientation + double inertia[3]; // diagonal inertia (in i-frame) + mjsOrientation ialt; // inertial frame alternative orientation + double fullinertia[6]; // non-axis-aligned inertia matrix + + // other + mjtByte mocap; // is this a mocap body + double gravcomp; // gravity compensation + mjDoubleVec* userdata; // user data + mjtByte explicitinertial; // whether to save the body with explicit inertial clause + mjsPlugin plugin; // passive force plugin + mjString* info; // message appended to compiler errors +} mjsBody; + + +typedef struct mjsFrame_ { // frame specification + mjsElement* element; // element type + mjString* name; // name + mjString* childclass; // childclass name + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + mjString* info; // message appended to compiler errors +} mjsFrame; + + +typedef struct mjsJoint_ { // joint specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + mjtJoint type; // joint type + + // kinematics + double pos[3]; // anchor position + double axis[3]; // joint axis + double ref; // value at reference configuration: qpos0 + + // stiffness + double stiffness; // stiffness coefficient + double springref; // spring reference value: qpos_spring + double springdamper[2]; // timeconst, dampratio + + // limits + int limited; // does joint have limits (mjtLimited) + double range[2]; // joint limits + double margin; // margin value for joint limit detection + mjtNum solref_limit[mjNREF]; // solver reference: joint limits + mjtNum solimp_limit[mjNIMP]; // solver impedance: joint limits + int actfrclimited; // are actuator forces on joint limited (mjtLimited) + double actfrcrange[2]; // actuator force limits + + // dof properties + double armature; // armature inertia (mass for slider) + double damping; // damping coefficient + double frictionloss; // friction loss + mjtNum solref_friction[mjNREF]; // solver reference: dof friction + mjtNum solimp_friction[mjNIMP]; // solver impedance: dof friction + + // other + int group; // group + mjtByte actgravcomp; // is gravcomp force applied via actuators + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors +} mjsJoint; + + +typedef struct mjsGeom_ { // geom specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // classname + mjtGeom type; // geom type + + // frame, size + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + double fromto[6]; // alternative for capsule, cylinder, box, ellipsoid + double size[3]; // type-specific size + + // contact related + int contype; // contact type + int conaffinity; // contact affinity + int condim; // contact dimensionality + int priority; // contact priority + double friction[3]; // one-sided friction coefficients: slide, roll, spin + double solmix; // solver mixing for contact pairs + mjtNum solref[mjNREF]; // solver reference + mjtNum solimp[mjNIMP]; // solver impedance + double margin; // margin for contact detection + double gap; // include in solver if dist < margin-gap + + // inertia inference + double mass; // used to compute density + double density; // used to compute mass and inertia from volume or surface + mjtGeomInertia typeinertia; // selects between surface and volume inertia + + // fluid forces + mjtNum fluid_ellipsoid; // whether ellipsoid-fluid model is active + mjtNum fluid_coefs[5]; // ellipsoid-fluid interaction coefs + + // visual + mjString* material; // name of material + float rgba[4]; // rgba when material is omitted + int group; // group + + // other + mjString* hfieldname; // heightfield attached to geom + mjString* meshname; // mesh attached to geom + double fitscale; // scale mesh uniformly + mjDoubleVec* userdata; // user data + mjsPlugin plugin; // sdf plugin + mjString* info; // message appended to compiler errors +} mjsGeom; + + +typedef struct mjsSite_ { // site specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // frame, size + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + double fromto[6]; // alternative for capsule, cylinder, box, ellipsoid + double size[3]; // geom size + + // visual + mjtGeom type; // geom type + mjString* material; // name of material + int group; // group + float rgba[4]; // rgba when material is omitted + + // other + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors +} mjsSite; + + +typedef struct mjsCamera_ { // camera specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // extrinsics + double pos[3]; // position + double quat[4]; // orientation + mjsOrientation alt; // alternative orientation + mjtCamLight mode; // tracking mode + mjString* targetbody; // target body for tracking/targeting + + // intrinsics + double fovy; // y-field of view + double ipd; // inter-pupilary distance + float intrinsic[4]; // camera intrinsics (length) + float sensor_size[2]; // sensor size (length) + float resolution[2]; // resolution (pixel) + float focal_length[2]; // focal length (length) + float focal_pixel[2]; // focal length (pixel) + float principal_length[2]; // principal point (length) + float principal_pixel[2]; // principal point (pixel) + + // other + mjDoubleVec* userdata; // user data + mjString* info; // message appended to compiler errors +} mjsCamera; + + +typedef struct mjsLight_ { // light specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // frame + double pos[3]; // position + double dir[3]; // direction + mjtCamLight mode; // tracking mode + mjString* targetbody; // target body for targeting + + // intrinsics + mjtByte active; // is light active + mjtByte directional; // is light directional or spot + mjtByte castshadow; // does light cast shadows + double bulbradius; // bulb radius, for soft shadows + float attenuation[3]; // OpenGL attenuation (quadratic model) + float cutoff; // OpenGL cutoff + float exponent; // OpenGL exponent + float ambient[3]; // ambient color + float diffuse[3]; // diffuse color + float specular[3]; // specular color + + // other + mjString* info; // message appended to compiler errorsx +} mjsLight; + + +typedef struct mjsFlex_ { // flex specification + mjsElement* element; // element type + mjString* name; // name + mjString* classname; // class name + + // contact properties + int contype; // contact type + int conaffinity; // contact affinity + int condim; // contact dimensionality + int priority; // contact priority + double friction[3]; // one-sided friction coefficients: slide, roll, spin + double solmix; // solver mixing for contact pairs + mjtNum solref[mjNREF]; // solver reference + mjtNum solimp[mjNIMP]; // solver impedance + double margin; // margin for contact detection + double gap; // include in solver if dist - - -// this is a C-API -#ifdef __cplusplus -extern "C" { -#endif - // header version; should match the library version as returned by mj_version() #define mjVERSION_HEADER 317 @@ -32,15 +24,21 @@ extern "C" { // type definitions #include +#include #include #include #include #include +#include #include #include #include #include +// this is a C-API +#ifdef __cplusplus +extern "C" { +#endif // user error and memory handlers MJAPI extern void (*mju_user_error)(const char*); @@ -97,6 +95,7 @@ MJAPI void mj_deleteVFS(mjVFS* vfs); // deprecated: use mj_copyBufferVFS. MJAPI int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize); + //---------------------------------- Parse and compile --------------------------------------------- // Parse XML file in MJCF or URDF format, compile it, return low-level model. @@ -104,6 +103,18 @@ MJAPI int mj_makeEmptyFileVFS(mjVFS* vfs, const char* filename, int filesize); // If error is not NULL, it must have size error_sz. MJAPI mjModel* mj_loadXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); +// Parse spec from XML file. +MJAPI mjSpec* mj_parseXML(const char* filename, const mjVFS* vfs, char* error, int error_sz); + +// Parse spec from XML string. +MJAPI mjSpec* mj_parseXMLString(const char* xml, const mjVFS* vfs, char* error, int error_sz); + +// Compile spec to model. +MJAPI mjModel* mj_compile(mjSpec* s, const mjVFS* vfs); + +// Recompile spec to model, preserving the state. +MJAPI void mj_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d); + // Update XML data structures with info from low-level model, save as MJCF. // If error is not NULL, it must have size error_sz. MJAPI int mj_saveLastXML(const char* filename, const mjModel* m, char* error, int error_sz); @@ -111,9 +122,14 @@ MJAPI int mj_saveLastXML(const char* filename, const mjModel* m, char* error, in // Free last XML model if loaded. Called internally at each load. MJAPI void mj_freeLastXML(void); -// Print internal XML schema as plain text or HTML, with style-padding or  . -MJAPI int mj_printSchema(const char* filename, char* buffer, int buffer_sz, - int flg_html, int flg_pad); +// Copy (possibly modified) model fields back into spec. +MJAPI void mj_copyBack(mjSpec* s, const mjModel* m); + +// Save spec to XML string, return 1 on success, 0 otherwise. +MJAPI int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int error_sz); + +// Save spec to XML file, return 1 on success, 0 otherwise. +MJAPI int mj_saveXML(const mjSpec* s, const char* filename, char* error, int error_sz); //---------------------------------- Main simulation ----------------------------------------------- @@ -221,6 +237,15 @@ MJAPI void mj_setConst(mjModel* m, mjData* d); MJAPI int mj_setLengthRange(mjModel* m, mjData* d, int index, const mjLROpt* opt, char* error, int error_sz); +// Create empty spec. +MJAPI mjSpec* mj_makeSpec(void); + +// Copy spec. +MJAPI mjSpec* mj_copySpec(const mjSpec* s); + +// Free memory allocation in mjSpec. +MJAPI void mj_deleteSpec(mjSpec* s); + //---------------------------------- Printing ------------------------------------------------------ @@ -246,6 +271,10 @@ MJAPI void mju_printMat(const mjtNum* mat, int nr, int nc); MJAPI void mju_printMatSparse(const mjtNum* mat, int nr, const int* rownnz, const int* rowadr, const int* colind); +// Print internal XML schema as plain text or HTML, with style-padding or  . +MJAPI int mj_printSchema(const char* filename, char* buffer, int buffer_sz, + int flg_html, int flg_pad); + //---------------------------------- Components ---------------------------------------------------- @@ -510,7 +539,7 @@ MJAPI int mj_version(void); MJAPI const char* mj_versionString(void); -//---------------------------------- Ray collisions ------------------------------------------------ +//---------------------------------- Ray casting --------------------------------------------------- // Intersect multiple rays emanating from a single point. // Similar semantics to mj_ray, but vec is an array of (nray x 3) directions. @@ -852,6 +881,12 @@ MJAPI void mj_warning(mjData* d, int warning, int info); // Write [datetime, type: message] to MUJOCO_LOG.TXT. MJAPI void mju_writeLog(const char* type, const char* msg); +// Get compiler error message from spec. +MJAPI const char* mjs_getError(mjSpec* s); + +// Return 1 if compiler error is a warning. +MJAPI int mjs_isWarning(mjSpec* s); + //---------------------------------- Standard math ------------------------------------------------- @@ -1037,6 +1072,7 @@ MJAPI void mju_transformSpatial(mjtNum res[6], const mjtNum vec[6], int flg_forc const mjtNum newpos[3], const mjtNum oldpos[3], const mjtNum rotnew2old[9]); + //---------------------------------- Quaternions --------------------------------------------------- // Rotate vector by quaternion. @@ -1079,6 +1115,7 @@ MJAPI void mju_quatZ2Vec(mjtNum quat[4], const mjtNum vec[3]); // seq[0,1,2] must be in 'xyzXYZ', lower/upper-case mean intrinsic/extrinsic rotations. MJAPI void mju_euler2Quat(mjtNum quat[4], const mjtNum euler[3], const char* seq); + //---------------------------------- Poses --------------------------------------------------------- // Multiply two poses. @@ -1166,7 +1203,8 @@ MJAPI int mju_boxQP(mjtNum* res, mjtNum* R, int* index, const mjtNum* H, const m MJAPI void mju_boxQPmalloc(mjtNum** res, mjtNum** R, int** index, mjtNum** H, mjtNum** g, int n, mjtNum** lower, mjtNum** upper); -//---------------------- Miscellaneous ------------------------------------------------------------- + +//---------------------------------- Miscellaneous ------------------------------------------------- // Muscle active force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax). MJAPI mjtNum mju_muscleGain(mjtNum len, mjtNum vel, const mjtNum lengthrange[2], @@ -1252,7 +1290,7 @@ MJAPI char* mju_strncpy(char *dst, const char *src, int n); MJAPI mjtNum mju_sigmoid(mjtNum x); -//---------------------- Derivatives --------------------------------------------------------------- +//---------------------------------- Derivatives --------------------------------------------------- // Finite differenced transition matrices (control theory notation) // d(x_next) = A*dx + B*du @@ -1292,7 +1330,8 @@ MJAPI void mjd_subQuat(const mjtNum qa[4], const mjtNum qb[4], mjtNum Da[9], mjt MJAPI void mjd_quatIntegrate(const mjtNum vel[3], mjtNum scale, mjtNum Dquat[9], mjtNum Dvel[9], mjtNum Dscale[3]); -//---------------------- Plugins ------------------------------------------------------------------- + +//---------------------------------- Plugins ------------------------------------------------------- // Set default plugin definition. MJAPI void mjp_defaultPlugin(mjpPlugin* plugin); @@ -1333,7 +1372,8 @@ MJAPI const mjpResourceProvider* mjp_getResourceProvider(const char* resource_na // If invalid slot number, return NULL. MJAPI const mjpResourceProvider* mjp_getResourceProviderAtSlot(int slot); -//---------------------- Thread ------------------------------------------------------------------- + +//---------------------------------- Threads ------------------------------------------------------- // Create a thread pool with the specified number of threads running. MJAPI mjThreadPool* mju_threadPoolCreate(size_t number_of_threads); @@ -1353,10 +1393,301 @@ MJAPI void mju_defaultTask(mjTask* task); // Wait for a task to complete. MJAPI void mju_taskJoin(mjTask* task); -//---------------------- Sanitizer instrumentation helpers ----------------------------------------- -// -// Most MuJoCo users can ignore these functions, the following comments are aimed primarily at -// MuJoCo developers. + +//---------------------------------- Attachment ---------------------------------------------------- + +// Attach child body to a parent frame, return 0 on success. +MJAPI int mjs_attachBody(mjsFrame* parent, const mjsBody* child, + const char* prefix, const char* suffix); + +// Attach child frame to a parent body, return 0 on success. +MJAPI int mjs_attachFrame(mjsBody* parent, const mjsFrame* child, + const char* prefix, const char* suffix); + +// Detach body from mjSpec, remove all references and delete the body, return 0 on success. +MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b); + + +//---------------------------------- Tree elements ------------------------------------------------- + +// Add child body to body, return child. +MJAPI mjsBody* mjs_addBody(mjsBody* body, mjsDefault* def); + +// Add site to body, return site spec. +MJAPI mjsSite* mjs_addSite(mjsBody* body, mjsDefault* def); + +// Add joint to body. +MJAPI mjsJoint* mjs_addJoint(mjsBody* body, mjsDefault* def); + +// Add freejoint to body. +MJAPI mjsJoint* mjs_addFreeJoint(mjsBody* body); + +// Add geom to body. +MJAPI mjsGeom* mjs_addGeom(mjsBody* body, mjsDefault* def); + +// Add camera to body. +MJAPI mjsCamera* mjs_addCamera(mjsBody* body, mjsDefault* def); + +// Add light to body. +MJAPI mjsLight* mjs_addLight(mjsBody* body, mjsDefault* def); + +// Add frame to body. +MJAPI mjsFrame* mjs_addFrame(mjsBody* body, mjsFrame* parentframe); + +// Delete object corresponding to the given element. +MJAPI void mjs_delete(mjsElement* element); + + +//---------------------------------- Non-tree elements --------------------------------------------- + +// Add actuator. +MJAPI mjsActuator* mjs_addActuator(mjSpec* s, mjsDefault* def); + +// Add sensor. +MJAPI mjsSensor* mjs_addSensor(mjSpec* s); + +// Add flex. +MJAPI mjsFlex* mjs_addFlex(mjSpec* s); + +// Add contact pair. +MJAPI mjsPair* mjs_addPair(mjSpec* s, mjsDefault* def); + +// Add excluded body pair. +MJAPI mjsExclude* mjs_addExclude(mjSpec* s); + +// Add equality. +MJAPI mjsEquality* mjs_addEquality(mjSpec* s, mjsDefault* def); + +// Add tendon. +MJAPI mjsTendon* mjs_addTendon(mjSpec* s, mjsDefault* def); + +// Wrap site using tendon. +MJAPI mjsWrap* mjs_wrapSite(mjsTendon* tendon, const char* name); + +// Wrap geom using tendon. +MJAPI mjsWrap* mjs_wrapGeom(mjsTendon* tendon, const char* name, const char* sidesite); + +// Wrap joint using tendon. +MJAPI mjsWrap* mjs_wrapJoint(mjsTendon* tendon, const char* name, double coef); + +// Wrap pulley using tendon. +MJAPI mjsWrap* mjs_wrapPulley(mjsTendon* tendon, double divisor); + +// Add numeric. +MJAPI mjsNumeric* mjs_addNumeric(mjSpec* s); + +// Add text. +MJAPI mjsText* mjs_addText(mjSpec* s); + +// Add tuple. +MJAPI mjsTuple* mjs_addTuple(mjSpec* s); + +// Add keyframe. +MJAPI mjsKey* mjs_addKey(mjSpec* s); + +// Add plugin. +MJAPI mjsPlugin* mjs_addPlugin(mjSpec* s); + +// Add default. +MJAPI mjsDefault* mjs_addDefault(mjSpec* s, const char* classname, int parentid, int* id); + + +//---------------------------------- Assets -------------------------------------------------------- + +// Add mesh. +MJAPI mjsMesh* mjs_addMesh(mjSpec* s, mjsDefault* def); + +// Add height field. +MJAPI mjsHField* mjs_addHField(mjSpec* s); + +// Add skin. +MJAPI mjsSkin* mjs_addSkin(mjSpec* s); + +// Add texture. +MJAPI mjsTexture* mjs_addTexture(mjSpec* s); + +// Add material. +MJAPI mjsMaterial* mjs_addMaterial(mjSpec* s, mjsDefault* def); + + +//---------------------------------- Find and get utilities ---------------------------------------- + +// Get spec from body. +MJAPI mjSpec* mjs_getSpec(mjsBody* body); + +// Find body in model by name. +MJAPI mjsBody* mjs_findBody(mjSpec* s, const char* name); + +// Find child body by name. +MJAPI mjsBody* mjs_findChild(mjsBody* body, const char* name); + +// Find mesh by name. +MJAPI mjsMesh* mjs_findMesh(mjSpec* s, const char* name); + +// Find frame by name. +MJAPI mjsFrame* mjs_findFrame(mjSpec* s, const char* name); + +// Get default corresponding to an element. +MJAPI mjsDefault* mjs_getDefault(mjsElement* element); + +// Find default in model by class name. +MJAPI mjsDefault* mjs_findDefault(mjSpec* s, const char* classname); + +// Get global default from model. +MJAPI mjsDefault* mjs_getSpecDefault(mjSpec* s); + +// Get element id. +MJAPI int mjs_getId(mjsElement* element); + +// Return body's first child of given type. +MJAPI mjsElement* mjs_firstChild(mjsBody* body, mjtObj type); + +// Return body's next child of the same type; return NULL if child is last. +MJAPI mjsElement* mjs_nextChild(mjsBody* body, mjsElement* child); + + +//---------------------------------- Attribute setters --------------------------------------------- + +// Copy text to string. +MJAPI void mjs_setString(mjString* dest, const char* text); + +// Split text to entries and copy to string vector. +MJAPI void mjs_setStringVec(mjStringVec* dest, const char* text); + +// Set entry in string vector. +MJAPI mjtByte mjs_setInStringVec(mjStringVec* dest, int i, const char* text); + +// Append text entry to string vector. +MJAPI void mjs_appendString(mjStringVec* dest, const char* text); + +// Copy int array to vector. +MJAPI void mjs_setInt(mjIntVec* dest, const int* array, int size); + +// Append int array to vector of arrays. +MJAPI void mjs_appendIntVec(mjIntVecVec* dest, const int* array, int size); + +// Copy float array to vector. +MJAPI void mjs_setFloat(mjFloatVec* dest, const float* array, int size); + +// Append float array to vector of arrays. +MJAPI void mjs_appendFloatVec(mjFloatVecVec* dest, const float* array, int size); + +// Copy double array to vector. +MJAPI void mjs_setDouble(mjDoubleVec* dest, const double* array, int size); + +// Set plugin attributes. +MJAPI void mjs_setPluginAttributes(mjsPlugin* plugin, void* attributes); + + +//---------------------------------- Attribute getters --------------------------------------------- + +// Get string contents. +MJAPI const char* mjs_getString(const mjString* source); + +// Get double array contents and optionally its size. +MJAPI const double* mjs_getDouble(const mjDoubleVec* source, int* size); + + +//---------------------------------- Spec utilities ------------------------------------------------ + +// Set active plugins. +MJAPI void mjs_setActivePlugins(mjSpec* s, void* activeplugins); + +// Set element's default. +MJAPI void mjs_setDefault(mjsElement* element, mjsDefault* def); + +// Set element's enlcosing frame. +MJAPI void mjs_setFrame(mjsElement* dest, mjsFrame* frame); + +// Resolve alternative orientations to quat, return error if any. +MJAPI const char* mjs_resolveOrientation(double quat[4], mjtByte degree, const char* sequence, + const mjsOrientation* orientation); + +// Compute quat and diag inertia from full inertia matrix, return error if any. +MJAPI const char* mjs_fullInertia(double quat[4], double inertia[3], const double fullinertia[6]); + + +//---------------------------------- Element initialization --------------------------------------- + +// Default spec attributes. +MJAPI void mjs_defaultSpec(mjSpec* spec); + +// Default orientation attributes. +MJAPI void mjs_defaultOrientation(mjsOrientation* orient); + +// Default body attributes. +MJAPI void mjs_defaultBody(mjsBody* body); + +// Default frame attributes. +MJAPI void mjs_defaultFrame(mjsFrame* frame); + +// Default joint attributes. +MJAPI void mjs_defaultJoint(mjsJoint* joint); + +// Default geom attributes. +MJAPI void mjs_defaultGeom(mjsGeom* geom); + +// Default site attributes. +MJAPI void mjs_defaultSite(mjsSite* site); + +// Default camera attributes. +MJAPI void mjs_defaultCamera(mjsCamera* camera); + +// Default light attributes. +MJAPI void mjs_defaultLight(mjsLight* light); + +// Default flex attributes. +MJAPI void mjs_defaultFlex(mjsFlex* flex); + +// Default mesh attributes. +MJAPI void mjs_defaultMesh(mjsMesh* mesh); + +// Default height field attributes. +MJAPI void mjs_defaultHField(mjsHField* hfield); + +// Default skin attributes. +MJAPI void mjs_defaultSkin(mjsSkin* skin); + +// Default texture attributes. +MJAPI void mjs_defaultTexture(mjsTexture* texture); + +// Default material attributes. +MJAPI void mjs_defaultMaterial(mjsMaterial* material); + +// Default pair attributes. +MJAPI void mjs_defaultPair(mjsPair* pair); + +// Default equality attributes. +MJAPI void mjs_defaultEquality(mjsEquality* equality); + +// Default tendon attributes. +MJAPI void mjs_defaultTendon(mjsTendon* tendon); + +// Default actuator attributes. +MJAPI void mjs_defaultActuator(mjsActuator* actuator); + +// Default sensor attributes. +MJAPI void mjs_defaultSensor(mjsSensor* sensor); + +// Default numeric attributes. +MJAPI void mjs_defaultNumeric(mjsNumeric* numeric); + +// Default text attributes. +MJAPI void mjs_defaultText(mjsText* text); + +// Default tuple attributes. +MJAPI void mjs_defaultTuple(mjsTuple* tuple); + +// Default keyframe attributes. +MJAPI void mjs_defaultKey(mjsKey* key); + +// Default plugin attributes. +MJAPI void mjs_defaultPlugin(mjsPlugin* plugin); + + +//---------------------------------- Sanitizer instrumentation ------------------------------------- + +// Most users can ignore these functions, the following comments are primarily for developers. // // When built and run under address sanitizer (asan), mj_markStack and mj_freeStack are instrumented // to detect leakage of mjData stack frames. When the compiler inlines several callees that call diff --git a/introspect/codegen/generate_structs.py b/introspect/codegen/generate_structs.py index 956b548f..42a5dcd1 100644 --- a/introspect/codegen/generate_structs.py +++ b/introspect/codegen/generate_structs.py @@ -123,7 +123,7 @@ class MjStructVisitor: """Makes a Decl object from a Clang AST RecordDecl node.""" name = f"{node['tagUsed']} {node['name']}" if 'name' in node else '' fields = [] - for child in node['inner']: + for child in node.get('inner', ()): child_kind = child.get('kind') if child_kind == 'FieldDecl': fields.append(self._make_field(child)) diff --git a/introspect/enums.py b/introspect/enums.py index 36a9e3a3..7d22f36f 100644 --- a/introspect/enums.py +++ b/introspect/enums.py @@ -686,6 +686,69 @@ ENUMS: Mapping[str, EnumDecl] = dict([ ('mjFONT_BIG', 2), ]), )), + ('mjtGeomInertia', + EnumDecl( + name='mjtGeomInertia', + declname='enum mjtGeomInertia_', + values=dict([ + ('mjINERTIA_VOLUME', 0), + ('mjINERTIA_SHELL', 1), + ]), + )), + ('mjtBuiltin', + EnumDecl( + name='mjtBuiltin', + declname='enum mjtBuiltin_', + values=dict([ + ('mjBUILTIN_NONE', 0), + ('mjBUILTIN_GRADIENT', 1), + ('mjBUILTIN_CHECKER', 2), + ('mjBUILTIN_FLAT', 3), + ]), + )), + ('mjtMark', + EnumDecl( + name='mjtMark', + declname='enum mjtMark_', + values=dict([ + ('mjMARK_NONE', 0), + ('mjMARK_EDGE', 1), + ('mjMARK_CROSS', 2), + ('mjMARK_RANDOM', 3), + ]), + )), + ('mjtLimited', + EnumDecl( + name='mjtLimited', + declname='enum mjtLimited_', + values=dict([ + ('mjLIMITED_FALSE', 0), + ('mjLIMITED_TRUE', 1), + ('mjLIMITED_AUTO', 2), + ]), + )), + ('mjtInertiaFromGeom', + EnumDecl( + name='mjtInertiaFromGeom', + declname='enum mjtInertiaFromGeom_', + values=dict([ + ('mjINERTIAFROMGEOM_FALSE', 0), + ('mjINERTIAFROMGEOM_TRUE', 1), + ('mjINERTIAFROMGEOM_AUTO', 2), + ]), + )), + ('mjtOrientation', + EnumDecl( + name='mjtOrientation', + declname='enum mjtOrientation_', + values=dict([ + ('mjORIENTATION_QUAT', 0), + ('mjORIENTATION_AXISANGLE', 1), + ('mjORIENTATION_XYAXES', 2), + ('mjORIENTATION_ZAXIS', 3), + ('mjORIENTATION_EULER', 4), + ]), + )), ('mjtButton', EnumDecl( name='mjtButton', diff --git a/introspect/functions.py b/introspect/functions.py index 2bb72499..a4cb5820 100644 --- a/introspect/functions.py +++ b/introspect/functions.py @@ -206,6 +206,124 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Parse XML file in MJCF or URDF format, compile it, return low-level model. If vfs is not NULL, look up files in vfs before reading from disk. If error is not NULL, it must have size error_sz.', # pylint: disable=line-too-long )), + ('mj_parseXML', + FunctionDecl( + name='mj_parseXML', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='filename', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='vfs', + type=PointerType( + inner_type=ValueType(name='mjVFS', is_const=True), + ), + ), + FunctionParameterDecl( + name='error', + type=PointerType( + inner_type=ValueType(name='char'), + ), + ), + FunctionParameterDecl( + name='error_sz', + type=ValueType(name='int'), + ), + ), + doc='Parse spec from XML file.', + )), + ('mj_parseXMLString', + FunctionDecl( + name='mj_parseXMLString', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='xml', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='vfs', + type=PointerType( + inner_type=ValueType(name='mjVFS', is_const=True), + ), + ), + FunctionParameterDecl( + name='error', + type=PointerType( + inner_type=ValueType(name='char'), + ), + ), + FunctionParameterDecl( + name='error_sz', + type=ValueType(name='int'), + ), + ), + doc='Parse spec from XML string.', + )), + ('mj_compile', + FunctionDecl( + name='mj_compile', + return_type=PointerType( + inner_type=ValueType(name='mjModel'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='vfs', + type=PointerType( + inner_type=ValueType(name='mjVFS', is_const=True), + ), + ), + ), + doc='Compile spec to model.', + )), + ('mj_recompile', + FunctionDecl( + name='mj_recompile', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='vfs', + type=PointerType( + inner_type=ValueType(name='mjVFS', is_const=True), + ), + ), + FunctionParameterDecl( + name='m', + type=PointerType( + inner_type=ValueType(name='mjModel'), + ), + ), + FunctionParameterDecl( + name='d', + type=PointerType( + inner_type=ValueType(name='mjData'), + ), + ), + ), + doc='Recompile spec to model, preserving the state.', + )), ('mj_saveLastXML', FunctionDecl( name='mj_saveLastXML', @@ -243,11 +361,71 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ parameters=(), doc='Free last XML model if loaded. Called internally at each load.', )), - ('mj_printSchema', + ('mj_copyBack', FunctionDecl( - name='mj_printSchema', + name='mj_copyBack', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='m', + type=PointerType( + inner_type=ValueType(name='mjModel', is_const=True), + ), + ), + ), + doc='Copy (possibly modified) model fields back into spec.', + )), + ('mj_saveXMLString', + FunctionDecl( + name='mj_saveXMLString', return_type=ValueType(name='int'), parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec', is_const=True), + ), + ), + FunctionParameterDecl( + name='xml', + type=PointerType( + inner_type=ValueType(name='char'), + ), + ), + FunctionParameterDecl( + name='xml_sz', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='error', + type=PointerType( + inner_type=ValueType(name='char'), + ), + ), + FunctionParameterDecl( + name='error_sz', + type=ValueType(name='int'), + ), + ), + doc='Save spec to XML string, return 1 on success, 0 otherwise.', + )), + ('mj_saveXML', + FunctionDecl( + name='mj_saveXML', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec', is_const=True), + ), + ), FunctionParameterDecl( name='filename', type=PointerType( @@ -255,25 +433,17 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), ), FunctionParameterDecl( - name='buffer', + name='error', type=PointerType( inner_type=ValueType(name='char'), ), ), FunctionParameterDecl( - name='buffer_sz', - type=ValueType(name='int'), - ), - FunctionParameterDecl( - name='flg_html', - type=ValueType(name='int'), - ), - FunctionParameterDecl( - name='flg_pad', + name='error_sz', type=ValueType(name='int'), ), ), - doc='Print internal XML schema as plain text or HTML, with style-padding or  .', # pylint: disable=line-too-long + doc='Save spec to XML file, return 1 on success, 0 otherwise.', )), ('mj_step', FunctionDecl( @@ -880,6 +1050,45 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Set actuator_lengthrange for specified actuator; return 1 if ok, 0 if error.', # pylint: disable=line-too-long )), + ('mj_makeSpec', + FunctionDecl( + name='mj_makeSpec', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=(), + doc='Create empty spec.', + )), + ('mj_copySpec', + FunctionDecl( + name='mj_copySpec', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec', is_const=True), + ), + ), + ), + doc='Copy spec.', + )), + ('mj_deleteSpec', + FunctionDecl( + name='mj_deleteSpec', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Free memory allocation in mjSpec.', + )), ('mj_printFormattedModel', FunctionDecl( name='mj_printFormattedModel', @@ -1042,6 +1251,38 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Print sparse matrix to screen.', )), + ('mj_printSchema', + FunctionDecl( + name='mj_printSchema', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='filename', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='buffer', + type=PointerType( + inner_type=ValueType(name='char'), + ), + ), + FunctionParameterDecl( + name='buffer_sz', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='flg_html', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='flg_pad', + type=ValueType(name='int'), + ), + ), + doc='Print internal XML schema as plain text or HTML, with style-padding or  .', # pylint: disable=line-too-long + )), ('mj_fwdPosition', FunctionDecl( name='mj_fwdPosition', @@ -5537,6 +5778,36 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Write [datetime, type: message] to MUJOCO_LOG.TXT.', )), + ('mjs_getError', + FunctionDecl( + name='mjs_getError', + return_type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Get compiler error message from spec.', + )), + ('mjs_isWarning', + FunctionDecl( + name='mjs_isWarning', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Return 1 if compiler error is a warning.', + )), ('mju_zero3', FunctionDecl( name='mju_zero3', @@ -8590,4 +8861,1658 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Wait for a task to complete.', )), + ('mjs_attachBody', + FunctionDecl( + name='mjs_attachBody', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='parent', + type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + ), + FunctionParameterDecl( + name='child', + type=PointerType( + inner_type=ValueType(name='mjsBody', is_const=True), + ), + ), + FunctionParameterDecl( + name='prefix', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='suffix', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Attach child body to a parent frame, return 0 on success.', + )), + ('mjs_attachFrame', + FunctionDecl( + name='mjs_attachFrame', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='parent', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='child', + type=PointerType( + inner_type=ValueType(name='mjsFrame', is_const=True), + ), + ), + FunctionParameterDecl( + name='prefix', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='suffix', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Attach child frame to a parent body, return 0 on success.', + )), + ('mjs_detachBody', + FunctionDecl( + name='mjs_detachBody', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='b', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + ), + doc='Detach body from mjSpec, remove all references and delete the body, return 0 on success.', # pylint: disable=line-too-long + )), + ('mjs_addBody', + FunctionDecl( + name='mjs_addBody', + return_type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add child body to body, return child.', + )), + ('mjs_addSite', + FunctionDecl( + name='mjs_addSite', + return_type=PointerType( + inner_type=ValueType(name='mjsSite'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add site to body, return site spec.', + )), + ('mjs_addJoint', + FunctionDecl( + name='mjs_addJoint', + return_type=PointerType( + inner_type=ValueType(name='mjsJoint'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add joint to body.', + )), + ('mjs_addFreeJoint', + FunctionDecl( + name='mjs_addFreeJoint', + return_type=PointerType( + inner_type=ValueType(name='mjsJoint'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + ), + doc='Add freejoint to body.', + )), + ('mjs_addGeom', + FunctionDecl( + name='mjs_addGeom', + return_type=PointerType( + inner_type=ValueType(name='mjsGeom'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add geom to body.', + )), + ('mjs_addCamera', + FunctionDecl( + name='mjs_addCamera', + return_type=PointerType( + inner_type=ValueType(name='mjsCamera'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add camera to body.', + )), + ('mjs_addLight', + FunctionDecl( + name='mjs_addLight', + return_type=PointerType( + inner_type=ValueType(name='mjsLight'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add light to body.', + )), + ('mjs_addFrame', + FunctionDecl( + name='mjs_addFrame', + return_type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='parentframe', + type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + ), + ), + doc='Add frame to body.', + )), + ('mjs_delete', + FunctionDecl( + name='mjs_delete', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + ), + ), + doc='Delete object corresponding to the given element.', + )), + ('mjs_addActuator', + FunctionDecl( + name='mjs_addActuator', + return_type=PointerType( + inner_type=ValueType(name='mjsActuator'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add actuator.', + )), + ('mjs_addSensor', + FunctionDecl( + name='mjs_addSensor', + return_type=PointerType( + inner_type=ValueType(name='mjsSensor'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add sensor.', + )), + ('mjs_addFlex', + FunctionDecl( + name='mjs_addFlex', + return_type=PointerType( + inner_type=ValueType(name='mjsFlex'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add flex.', + )), + ('mjs_addPair', + FunctionDecl( + name='mjs_addPair', + return_type=PointerType( + inner_type=ValueType(name='mjsPair'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add contact pair.', + )), + ('mjs_addExclude', + FunctionDecl( + name='mjs_addExclude', + return_type=PointerType( + inner_type=ValueType(name='mjsExclude'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add excluded body pair.', + )), + ('mjs_addEquality', + FunctionDecl( + name='mjs_addEquality', + return_type=PointerType( + inner_type=ValueType(name='mjsEquality'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add equality.', + )), + ('mjs_addTendon', + FunctionDecl( + name='mjs_addTendon', + return_type=PointerType( + inner_type=ValueType(name='mjsTendon'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add tendon.', + )), + ('mjs_wrapSite', + FunctionDecl( + name='mjs_wrapSite', + return_type=PointerType( + inner_type=ValueType(name='mjsWrap'), + ), + parameters=( + FunctionParameterDecl( + name='tendon', + type=PointerType( + inner_type=ValueType(name='mjsTendon'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Wrap site using tendon.', + )), + ('mjs_wrapGeom', + FunctionDecl( + name='mjs_wrapGeom', + return_type=PointerType( + inner_type=ValueType(name='mjsWrap'), + ), + parameters=( + FunctionParameterDecl( + name='tendon', + type=PointerType( + inner_type=ValueType(name='mjsTendon'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='sidesite', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Wrap geom using tendon.', + )), + ('mjs_wrapJoint', + FunctionDecl( + name='mjs_wrapJoint', + return_type=PointerType( + inner_type=ValueType(name='mjsWrap'), + ), + parameters=( + FunctionParameterDecl( + name='tendon', + type=PointerType( + inner_type=ValueType(name='mjsTendon'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='coef', + type=ValueType(name='double'), + ), + ), + doc='Wrap joint using tendon.', + )), + ('mjs_wrapPulley', + FunctionDecl( + name='mjs_wrapPulley', + return_type=PointerType( + inner_type=ValueType(name='mjsWrap'), + ), + parameters=( + FunctionParameterDecl( + name='tendon', + type=PointerType( + inner_type=ValueType(name='mjsTendon'), + ), + ), + FunctionParameterDecl( + name='divisor', + type=ValueType(name='double'), + ), + ), + doc='Wrap pulley using tendon.', + )), + ('mjs_addNumeric', + FunctionDecl( + name='mjs_addNumeric', + return_type=PointerType( + inner_type=ValueType(name='mjsNumeric'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add numeric.', + )), + ('mjs_addText', + FunctionDecl( + name='mjs_addText', + return_type=PointerType( + inner_type=ValueType(name='mjsText'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add text.', + )), + ('mjs_addTuple', + FunctionDecl( + name='mjs_addTuple', + return_type=PointerType( + inner_type=ValueType(name='mjsTuple'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add tuple.', + )), + ('mjs_addKey', + FunctionDecl( + name='mjs_addKey', + return_type=PointerType( + inner_type=ValueType(name='mjsKey'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add keyframe.', + )), + ('mjs_addPlugin', + FunctionDecl( + name='mjs_addPlugin', + return_type=PointerType( + inner_type=ValueType(name='mjsPlugin'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add plugin.', + )), + ('mjs_addDefault', + FunctionDecl( + name='mjs_addDefault', + return_type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='classname', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='parentid', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='id', + type=PointerType( + inner_type=ValueType(name='int'), + ), + ), + ), + doc='Add default.', + )), + ('mjs_addMesh', + FunctionDecl( + name='mjs_addMesh', + return_type=PointerType( + inner_type=ValueType(name='mjsMesh'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add mesh.', + )), + ('mjs_addHField', + FunctionDecl( + name='mjs_addHField', + return_type=PointerType( + inner_type=ValueType(name='mjsHField'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add height field.', + )), + ('mjs_addSkin', + FunctionDecl( + name='mjs_addSkin', + return_type=PointerType( + inner_type=ValueType(name='mjsSkin'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add skin.', + )), + ('mjs_addTexture', + FunctionDecl( + name='mjs_addTexture', + return_type=PointerType( + inner_type=ValueType(name='mjsTexture'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Add texture.', + )), + ('mjs_addMaterial', + FunctionDecl( + name='mjs_addMaterial', + return_type=PointerType( + inner_type=ValueType(name='mjsMaterial'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc='Add material.', + )), + ('mjs_getSpec', + FunctionDecl( + name='mjs_getSpec', + return_type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + ), + doc='Get spec from body.', + )), + ('mjs_findBody', + FunctionDecl( + name='mjs_findBody', + return_type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Find body in model by name.', + )), + ('mjs_findChild', + FunctionDecl( + name='mjs_findChild', + return_type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Find child body by name.', + )), + ('mjs_findMesh', + FunctionDecl( + name='mjs_findMesh', + return_type=PointerType( + inner_type=ValueType(name='mjsMesh'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Find mesh by name.', + )), + ('mjs_findFrame', + FunctionDecl( + name='mjs_findFrame', + return_type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Find frame by name.', + )), + ('mjs_getDefault', + FunctionDecl( + name='mjs_getDefault', + return_type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + parameters=( + FunctionParameterDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + ), + ), + doc='Get default corresponding to an element.', + )), + ('mjs_findDefault', + FunctionDecl( + name='mjs_findDefault', + return_type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='classname', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Find default in model by class name.', + )), + ('mjs_getSpecDefault', + FunctionDecl( + name='mjs_getSpecDefault', + return_type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Get global default from model.', + )), + ('mjs_getId', + FunctionDecl( + name='mjs_getId', + return_type=ValueType(name='int'), + parameters=( + FunctionParameterDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + ), + ), + doc='Get element id.', + )), + ('mjs_firstChild', + FunctionDecl( + name='mjs_firstChild', + return_type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='type', + type=ValueType(name='mjtObj'), + ), + ), + doc="Return body's first child of given type.", + )), + ('mjs_nextChild', + FunctionDecl( + name='mjs_nextChild', + return_type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + FunctionParameterDecl( + name='child', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + ), + ), + doc="Return body's next child of the same type; return NULL if child is last.", # pylint: disable=line-too-long + )), + ('mjs_setString', + FunctionDecl( + name='mjs_setString', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + ), + FunctionParameterDecl( + name='text', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Copy text to string.', + )), + ('mjs_setStringVec', + FunctionDecl( + name='mjs_setStringVec', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjStringVec'), + ), + ), + FunctionParameterDecl( + name='text', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Split text to entries and copy to string vector.', + )), + ('mjs_setInStringVec', + FunctionDecl( + name='mjs_setInStringVec', + return_type=ValueType(name='mjtByte'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjStringVec'), + ), + ), + FunctionParameterDecl( + name='i', + type=ValueType(name='int'), + ), + FunctionParameterDecl( + name='text', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Set entry in string vector.', + )), + ('mjs_appendString', + FunctionDecl( + name='mjs_appendString', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjStringVec'), + ), + ), + FunctionParameterDecl( + name='text', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + ), + doc='Append text entry to string vector.', + )), + ('mjs_setInt', + FunctionDecl( + name='mjs_setInt', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjIntVec'), + ), + ), + FunctionParameterDecl( + name='array', + type=PointerType( + inner_type=ValueType(name='int', is_const=True), + ), + ), + FunctionParameterDecl( + name='size', + type=ValueType(name='int'), + ), + ), + doc='Copy int array to vector.', + )), + ('mjs_appendIntVec', + FunctionDecl( + name='mjs_appendIntVec', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjIntVecVec'), + ), + ), + FunctionParameterDecl( + name='array', + type=PointerType( + inner_type=ValueType(name='int', is_const=True), + ), + ), + FunctionParameterDecl( + name='size', + type=ValueType(name='int'), + ), + ), + doc='Append int array to vector of arrays.', + )), + ('mjs_setFloat', + FunctionDecl( + name='mjs_setFloat', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjFloatVec'), + ), + ), + FunctionParameterDecl( + name='array', + type=PointerType( + inner_type=ValueType(name='float', is_const=True), + ), + ), + FunctionParameterDecl( + name='size', + type=ValueType(name='int'), + ), + ), + doc='Copy float array to vector.', + )), + ('mjs_appendFloatVec', + FunctionDecl( + name='mjs_appendFloatVec', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjFloatVecVec'), + ), + ), + FunctionParameterDecl( + name='array', + type=PointerType( + inner_type=ValueType(name='float', is_const=True), + ), + ), + FunctionParameterDecl( + name='size', + type=ValueType(name='int'), + ), + ), + doc='Append float array to vector of arrays.', + )), + ('mjs_setDouble', + FunctionDecl( + name='mjs_setDouble', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjDoubleVec'), + ), + ), + FunctionParameterDecl( + name='array', + type=PointerType( + inner_type=ValueType(name='double', is_const=True), + ), + ), + FunctionParameterDecl( + name='size', + type=ValueType(name='int'), + ), + ), + doc='Copy double array to vector.', + )), + ('mjs_setPluginAttributes', + FunctionDecl( + name='mjs_setPluginAttributes', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='plugin', + type=PointerType( + inner_type=ValueType(name='mjsPlugin'), + ), + ), + FunctionParameterDecl( + name='attributes', + type=PointerType( + inner_type=ValueType(name='void'), + ), + ), + ), + doc='Set plugin attributes.', + )), + ('mjs_getString', + FunctionDecl( + name='mjs_getString', + return_type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + parameters=( + FunctionParameterDecl( + name='source', + type=PointerType( + inner_type=ValueType(name='mjString', is_const=True), + ), + ), + ), + doc='Get string contents.', + )), + ('mjs_getDouble', + FunctionDecl( + name='mjs_getDouble', + return_type=PointerType( + inner_type=ValueType(name='double', is_const=True), + ), + parameters=( + FunctionParameterDecl( + name='source', + type=PointerType( + inner_type=ValueType(name='mjDoubleVec', is_const=True), + ), + ), + FunctionParameterDecl( + name='size', + type=PointerType( + inner_type=ValueType(name='int'), + ), + ), + ), + doc='Get double array contents and optionally its size.', + )), + ('mjs_setActivePlugins', + FunctionDecl( + name='mjs_setActivePlugins', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='s', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + FunctionParameterDecl( + name='activeplugins', + type=PointerType( + inner_type=ValueType(name='void'), + ), + ), + ), + doc='Set active plugins.', + )), + ('mjs_setDefault', + FunctionDecl( + name='mjs_setDefault', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + ), + FunctionParameterDecl( + name='def', + type=PointerType( + inner_type=ValueType(name='mjsDefault'), + ), + ), + ), + doc="Set element's default.", + )), + ('mjs_setFrame', + FunctionDecl( + name='mjs_setFrame', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='dest', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + ), + FunctionParameterDecl( + name='frame', + type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + ), + ), + doc="Set element's enlcosing frame.", + )), + ('mjs_resolveOrientation', + FunctionDecl( + name='mjs_resolveOrientation', + return_type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + parameters=( + FunctionParameterDecl( + name='quat', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + ), + FunctionParameterDecl( + name='degree', + type=ValueType(name='mjtByte'), + ), + FunctionParameterDecl( + name='sequence', + type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + ), + FunctionParameterDecl( + name='orientation', + type=PointerType( + inner_type=ValueType(name='mjsOrientation', is_const=True), + ), + ), + ), + doc='Resolve alternative orientations to quat, return error if any.', + )), + ('mjs_fullInertia', + FunctionDecl( + name='mjs_fullInertia', + return_type=PointerType( + inner_type=ValueType(name='char', is_const=True), + ), + parameters=( + FunctionParameterDecl( + name='quat', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + ), + FunctionParameterDecl( + name='inertia', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + ), + FunctionParameterDecl( + name='fullinertia', + type=ArrayType( + inner_type=ValueType(name='double', is_const=True), + extents=(6,), + ), + ), + ), + doc='Compute quat and diag inertia from full inertia matrix, return error if any.', # pylint: disable=line-too-long + )), + ('mjs_defaultSpec', + FunctionDecl( + name='mjs_defaultSpec', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='spec', + type=PointerType( + inner_type=ValueType(name='mjSpec'), + ), + ), + ), + doc='Default spec attributes.', + )), + ('mjs_defaultOrientation', + FunctionDecl( + name='mjs_defaultOrientation', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='orient', + type=PointerType( + inner_type=ValueType(name='mjsOrientation'), + ), + ), + ), + doc='Default orientation attributes.', + )), + ('mjs_defaultBody', + FunctionDecl( + name='mjs_defaultBody', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='body', + type=PointerType( + inner_type=ValueType(name='mjsBody'), + ), + ), + ), + doc='Default body attributes.', + )), + ('mjs_defaultFrame', + FunctionDecl( + name='mjs_defaultFrame', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='frame', + type=PointerType( + inner_type=ValueType(name='mjsFrame'), + ), + ), + ), + doc='Default frame attributes.', + )), + ('mjs_defaultJoint', + FunctionDecl( + name='mjs_defaultJoint', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='joint', + type=PointerType( + inner_type=ValueType(name='mjsJoint'), + ), + ), + ), + doc='Default joint attributes.', + )), + ('mjs_defaultGeom', + FunctionDecl( + name='mjs_defaultGeom', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='geom', + type=PointerType( + inner_type=ValueType(name='mjsGeom'), + ), + ), + ), + doc='Default geom attributes.', + )), + ('mjs_defaultSite', + FunctionDecl( + name='mjs_defaultSite', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='site', + type=PointerType( + inner_type=ValueType(name='mjsSite'), + ), + ), + ), + doc='Default site attributes.', + )), + ('mjs_defaultCamera', + FunctionDecl( + name='mjs_defaultCamera', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='camera', + type=PointerType( + inner_type=ValueType(name='mjsCamera'), + ), + ), + ), + doc='Default camera attributes.', + )), + ('mjs_defaultLight', + FunctionDecl( + name='mjs_defaultLight', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='light', + type=PointerType( + inner_type=ValueType(name='mjsLight'), + ), + ), + ), + doc='Default light attributes.', + )), + ('mjs_defaultFlex', + FunctionDecl( + name='mjs_defaultFlex', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='flex', + type=PointerType( + inner_type=ValueType(name='mjsFlex'), + ), + ), + ), + doc='Default flex attributes.', + )), + ('mjs_defaultMesh', + FunctionDecl( + name='mjs_defaultMesh', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='mesh', + type=PointerType( + inner_type=ValueType(name='mjsMesh'), + ), + ), + ), + doc='Default mesh attributes.', + )), + ('mjs_defaultHField', + FunctionDecl( + name='mjs_defaultHField', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='hfield', + type=PointerType( + inner_type=ValueType(name='mjsHField'), + ), + ), + ), + doc='Default height field attributes.', + )), + ('mjs_defaultSkin', + FunctionDecl( + name='mjs_defaultSkin', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='skin', + type=PointerType( + inner_type=ValueType(name='mjsSkin'), + ), + ), + ), + doc='Default skin attributes.', + )), + ('mjs_defaultTexture', + FunctionDecl( + name='mjs_defaultTexture', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='texture', + type=PointerType( + inner_type=ValueType(name='mjsTexture'), + ), + ), + ), + doc='Default texture attributes.', + )), + ('mjs_defaultMaterial', + FunctionDecl( + name='mjs_defaultMaterial', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='material', + type=PointerType( + inner_type=ValueType(name='mjsMaterial'), + ), + ), + ), + doc='Default material attributes.', + )), + ('mjs_defaultPair', + FunctionDecl( + name='mjs_defaultPair', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='pair', + type=PointerType( + inner_type=ValueType(name='mjsPair'), + ), + ), + ), + doc='Default pair attributes.', + )), + ('mjs_defaultEquality', + FunctionDecl( + name='mjs_defaultEquality', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='equality', + type=PointerType( + inner_type=ValueType(name='mjsEquality'), + ), + ), + ), + doc='Default equality attributes.', + )), + ('mjs_defaultTendon', + FunctionDecl( + name='mjs_defaultTendon', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='tendon', + type=PointerType( + inner_type=ValueType(name='mjsTendon'), + ), + ), + ), + doc='Default tendon attributes.', + )), + ('mjs_defaultActuator', + FunctionDecl( + name='mjs_defaultActuator', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='actuator', + type=PointerType( + inner_type=ValueType(name='mjsActuator'), + ), + ), + ), + doc='Default actuator attributes.', + )), + ('mjs_defaultSensor', + FunctionDecl( + name='mjs_defaultSensor', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='sensor', + type=PointerType( + inner_type=ValueType(name='mjsSensor'), + ), + ), + ), + doc='Default sensor attributes.', + )), + ('mjs_defaultNumeric', + FunctionDecl( + name='mjs_defaultNumeric', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='numeric', + type=PointerType( + inner_type=ValueType(name='mjsNumeric'), + ), + ), + ), + doc='Default numeric attributes.', + )), + ('mjs_defaultText', + FunctionDecl( + name='mjs_defaultText', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='text', + type=PointerType( + inner_type=ValueType(name='mjsText'), + ), + ), + ), + doc='Default text attributes.', + )), + ('mjs_defaultTuple', + FunctionDecl( + name='mjs_defaultTuple', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='tuple', + type=PointerType( + inner_type=ValueType(name='mjsTuple'), + ), + ), + ), + doc='Default tuple attributes.', + )), + ('mjs_defaultKey', + FunctionDecl( + name='mjs_defaultKey', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='key', + type=PointerType( + inner_type=ValueType(name='mjsKey'), + ), + ), + ), + doc='Default keyframe attributes.', + )), + ('mjs_defaultPlugin', + FunctionDecl( + name='mjs_defaultPlugin', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='plugin', + type=PointerType( + inner_type=ValueType(name='mjsPlugin'), + ), + ), + ), + doc='Default plugin attributes.', + )), ]) diff --git a/introspect/structs.py b/introspect/structs.py index f5c26e18..cdd5ff1b 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -8147,6 +8147,2997 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), ), )), + ('mjsElement', + StructDecl( + name='mjsElement', + declname='struct mjsElement_', + fields=( + StructFieldDecl( + name='elemtype', + type=ValueType(name='mjtObj'), + doc='element type', + ), + ), + )), + ('mjSpec', + StructDecl( + name='mjSpec', + declname='struct mjSpec_', + fields=( + StructFieldDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='element type', + ), + StructFieldDecl( + name='modelname', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='model name', + ), + StructFieldDecl( + name='autolimits', + type=ValueType(name='mjtByte'), + doc='infer "limited" attribute based on range', + ), + StructFieldDecl( + name='boundmass', + type=ValueType(name='double'), + doc='enforce minimum body mass', + ), + StructFieldDecl( + name='boundinertia', + type=ValueType(name='double'), + doc='enforce minimum body diagonal inertia', + ), + StructFieldDecl( + name='settotalmass', + type=ValueType(name='double'), + doc='rescale masses and inertias;<=0: ignore', + ), + StructFieldDecl( + name='balanceinertia', + type=ValueType(name='mjtByte'), + doc='automatically impose A + B >= C rule', + ), + StructFieldDecl( + name='strippath', + type=ValueType(name='mjtByte'), + doc='automatically strip paths from mesh files', + ), + StructFieldDecl( + name='fitaabb', + type=ValueType(name='mjtByte'), + doc='meshfit to aabb instead of inertia box', + ), + StructFieldDecl( + name='degree', + type=ValueType(name='mjtByte'), + doc='angles in radians or degrees', + ), + StructFieldDecl( + name='euler', + type=ArrayType( + inner_type=ValueType(name='char'), + extents=(3,), + ), + doc='sequence for euler rotations', + ), + StructFieldDecl( + name='meshdir', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='mesh and hfield directory', + ), + StructFieldDecl( + name='texturedir', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='texture directory', + ), + StructFieldDecl( + name='discardvisual', + type=ValueType(name='mjtByte'), + doc='discard visual geoms in parser', + ), + StructFieldDecl( + name='convexhull', + type=ValueType(name='mjtByte'), + doc='compute mesh convex hulls', + ), + StructFieldDecl( + name='usethread', + type=ValueType(name='mjtByte'), + doc='use multiple threads to speed up compiler', + ), + StructFieldDecl( + name='fusestatic', + type=ValueType(name='mjtByte'), + doc='fuse static bodies with parent', + ), + StructFieldDecl( + name='inertiafromgeom', + type=ValueType(name='int'), + doc='use geom inertias (mjtInertiaFromGeom)', + ), + StructFieldDecl( + name='inertiagrouprange', + type=ArrayType( + inner_type=ValueType(name='int'), + extents=(2,), + ), + doc='range of geom groups used to compute inertia', + ), + StructFieldDecl( + name='exactmeshinertia', + type=ValueType(name='mjtByte'), + doc='if false, use old formula', + ), + StructFieldDecl( + name='LRopt', + type=ValueType(name='mjLROpt'), + doc='options for lengthrange computation', + ), + StructFieldDecl( + name='option', + type=ValueType(name='mjOption'), + doc='physics options', + ), + StructFieldDecl( + name='visual', + type=ValueType(name='mjVisual'), + doc='visual options', + ), + StructFieldDecl( + name='stat', + type=ValueType(name='mjStatistic'), + doc='statistics override (if defined)', + ), + StructFieldDecl( + name='memory', + type=ValueType(name='size_t'), + doc='number of bytes in arena+stack memory', + ), + StructFieldDecl( + name='nemax', + type=ValueType(name='int'), + doc='max number of equality constraints', + ), + StructFieldDecl( + name='nuserdata', + type=ValueType(name='int'), + doc='number of mjtNums in userdata', + ), + StructFieldDecl( + name='nuser_body', + type=ValueType(name='int'), + doc='number of mjtNums in body_user', + ), + StructFieldDecl( + name='nuser_jnt', + type=ValueType(name='int'), + doc='number of mjtNums in jnt_user', + ), + StructFieldDecl( + name='nuser_geom', + type=ValueType(name='int'), + doc='number of mjtNums in geom_user', + ), + StructFieldDecl( + name='nuser_site', + type=ValueType(name='int'), + doc='number of mjtNums in site_user', + ), + StructFieldDecl( + name='nuser_cam', + type=ValueType(name='int'), + doc='number of mjtNums in cam_user', + ), + StructFieldDecl( + name='nuser_tendon', + type=ValueType(name='int'), + doc='number of mjtNums in tendon_user', + ), + StructFieldDecl( + name='nuser_actuator', + type=ValueType(name='int'), + doc='number of mjtNums in actuator_user', + ), + StructFieldDecl( + name='nuser_sensor', + type=ValueType(name='int'), + doc='number of mjtNums in sensor_user', + ), + StructFieldDecl( + name='nkey', + type=ValueType(name='int'), + doc='number of keyframes', + ), + StructFieldDecl( + name='njmax', + type=ValueType(name='int'), + doc='(deprecated) max number of constraints', + ), + StructFieldDecl( + name='nconmax', + type=ValueType(name='int'), + doc='(deprecated) max number of detected contacts', + ), + StructFieldDecl( + name='nstack', + type=ValueType(name='size_t'), + doc='(deprecated) number of mjtNums in mjData stack', + ), + StructFieldDecl( + name='comment', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='comment at top of XML', + ), + StructFieldDecl( + name='modelfiledir', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='path to model file', + ), + StructFieldDecl( + name='hasImplicitPluginElem', + type=ValueType(name='mjtByte'), + doc='already encountered an implicit plugin sensor/actuator', + ), + ), + )), + ('mjsOrientation', + StructDecl( + name='mjsOrientation', + declname='struct mjsOrientation_', + fields=( + StructFieldDecl( + name='type', + type=ValueType(name='mjtOrientation'), + doc='active orientation specifier', + ), + StructFieldDecl( + name='axisangle', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + doc='axis and angle', + ), + StructFieldDecl( + name='xyaxes', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(6,), + ), + doc='x and y axes', + ), + StructFieldDecl( + name='zaxis', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='z axis (minimal rotation)', + ), + StructFieldDecl( + name='euler', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='Euler angles', + ), + ), + )), + ('mjsPlugin', + StructDecl( + name='mjsPlugin', + declname='struct mjsPlugin_', + fields=( + StructFieldDecl( + name='instance', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='element type', + ), + StructFieldDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='name', + ), + StructFieldDecl( + name='instance_name', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='instance name', + ), + StructFieldDecl( + name='plugin_slot', + type=ValueType(name='int'), + doc='global registered slot number of the plugin', + ), + StructFieldDecl( + name='active', + type=ValueType(name='mjtByte'), + doc='is the plugin active', + ), + StructFieldDecl( + name='info', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='message appended to compiler errors', + ), + ), + )), + ('mjsBody', + StructDecl( + name='mjsBody', + declname='struct mjsBody_', + fields=( + StructFieldDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='element type', + ), + StructFieldDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='name', + ), + StructFieldDecl( + name='childclass', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='childclass name', + ), + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='frame position', + ), + StructFieldDecl( + name='quat', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + doc='frame orientation', + ), + StructFieldDecl( + name='alt', + type=ValueType(name='mjsOrientation'), + doc='frame alternative orientation', + ), + StructFieldDecl( + name='mass', + type=ValueType(name='double'), + doc='mass', + ), + StructFieldDecl( + name='ipos', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='inertial frame position', + ), + StructFieldDecl( + name='iquat', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + doc='inertial frame orientation', + ), + StructFieldDecl( + name='inertia', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='diagonal inertia (in i-frame)', + ), + StructFieldDecl( + name='ialt', + type=ValueType(name='mjsOrientation'), + doc='inertial frame alternative orientation', + ), + StructFieldDecl( + name='fullinertia', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(6,), + ), + doc='non-axis-aligned inertia matrix', + ), + StructFieldDecl( + name='mocap', + type=ValueType(name='mjtByte'), + doc='is this a mocap body', + ), + StructFieldDecl( + name='gravcomp', + type=ValueType(name='double'), + doc='gravity compensation', + ), + StructFieldDecl( + name='userdata', + type=PointerType( + inner_type=ValueType(name='mjDoubleVec'), + ), + doc='user data', + ), + StructFieldDecl( + name='explicitinertial', + type=ValueType(name='mjtByte'), + doc='whether to save the body with explicit inertial clause', + ), + StructFieldDecl( + name='plugin', + type=ValueType(name='mjsPlugin'), + doc='passive force plugin', + ), + StructFieldDecl( + name='info', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='message appended to compiler errors', + ), + ), + )), + ('mjsFrame', + StructDecl( + name='mjsFrame', + declname='struct mjsFrame_', + fields=( + StructFieldDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='element type', + ), + StructFieldDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='name', + ), + StructFieldDecl( + name='childclass', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='childclass name', + ), + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='position', + ), + StructFieldDecl( + name='quat', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + doc='orientation', + ), + StructFieldDecl( + name='alt', + type=ValueType(name='mjsOrientation'), + doc='alternative orientation', + ), + StructFieldDecl( + name='info', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='message appended to compiler errors', + ), + ), + )), + ('mjsJoint', + StructDecl( + name='mjsJoint', + declname='struct mjsJoint_', + fields=( + StructFieldDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='element type', + ), + StructFieldDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='name', + ), + StructFieldDecl( + name='classname', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='class name', + ), + StructFieldDecl( + name='type', + type=ValueType(name='mjtJoint'), + doc='joint type', + ), + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='anchor position', + ), + StructFieldDecl( + name='axis', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='joint axis', + ), + StructFieldDecl( + name='ref', + type=ValueType(name='double'), + doc='value at reference configuration: qpos0', + ), + StructFieldDecl( + name='stiffness', + type=ValueType(name='double'), + doc='stiffness coefficient', + ), + StructFieldDecl( + name='springref', + type=ValueType(name='double'), + doc='spring reference value: qpos_spring', + ), + StructFieldDecl( + name='springdamper', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(2,), + ), + doc='timeconst, dampratio', + ), + StructFieldDecl( + name='limited', + type=ValueType(name='int'), + doc='does joint have limits (mjtLimited)', + ), + StructFieldDecl( + name='range', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(2,), + ), + doc='joint limits', + ), + StructFieldDecl( + name='margin', + type=ValueType(name='double'), + doc='margin value for joint limit detection', + ), + StructFieldDecl( + name='solref_limit', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(2,), + ), + doc='solver reference: joint limits', + ), + StructFieldDecl( + name='solimp_limit', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(5,), + ), + doc='solver impedance: joint limits', + ), + StructFieldDecl( + name='actfrclimited', + type=ValueType(name='int'), + doc='are actuator forces on joint limited (mjtLimited)', + ), + StructFieldDecl( + name='actfrcrange', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(2,), + ), + doc='actuator force limits', + ), + StructFieldDecl( + name='armature', + type=ValueType(name='double'), + doc='armature inertia (mass for slider)', + ), + StructFieldDecl( + name='damping', + type=ValueType(name='double'), + doc='damping coefficient', + ), + StructFieldDecl( + name='frictionloss', + type=ValueType(name='double'), + doc='friction loss', + ), + StructFieldDecl( + name='solref_friction', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(2,), + ), + doc='solver reference: dof friction', + ), + StructFieldDecl( + name='solimp_friction', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(5,), + ), + doc='solver impedance: dof friction', + ), + StructFieldDecl( + name='group', + type=ValueType(name='int'), + doc='group', + ), + StructFieldDecl( + name='actgravcomp', + type=ValueType(name='mjtByte'), + doc='is gravcomp force applied via actuators', + ), + StructFieldDecl( + name='userdata', + type=PointerType( + inner_type=ValueType(name='mjDoubleVec'), + ), + doc='user data', + ), + StructFieldDecl( + name='info', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='message appended to compiler errors', + ), + ), + )), + ('mjsGeom', + StructDecl( + name='mjsGeom', + declname='struct mjsGeom_', + fields=( + StructFieldDecl( + name='element', + type=PointerType( + inner_type=ValueType(name='mjsElement'), + ), + doc='element type', + ), + StructFieldDecl( + name='name', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='name', + ), + StructFieldDecl( + name='classname', + type=PointerType( + inner_type=ValueType(name='mjString'), + ), + doc='classname', + ), + StructFieldDecl( + name='type', + type=ValueType(name='mjtGeom'), + doc='geom type', + ), + StructFieldDecl( + name='pos', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='position', + ), + StructFieldDecl( + name='quat', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(4,), + ), + doc='orientation', + ), + StructFieldDecl( + name='alt', + type=ValueType(name='mjsOrientation'), + doc='alternative orientation', + ), + StructFieldDecl( + name='fromto', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(6,), + ), + doc='alternative for capsule, cylinder, box, ellipsoid', + ), + StructFieldDecl( + name='size', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='type-specific size', + ), + StructFieldDecl( + name='contype', + type=ValueType(name='int'), + doc='contact type', + ), + StructFieldDecl( + name='conaffinity', + type=ValueType(name='int'), + doc='contact affinity', + ), + StructFieldDecl( + name='condim', + type=ValueType(name='int'), + doc='contact dimensionality', + ), + StructFieldDecl( + name='priority', + type=ValueType(name='int'), + doc='contact priority', + ), + StructFieldDecl( + name='friction', + type=ArrayType( + inner_type=ValueType(name='double'), + extents=(3,), + ), + doc='one-sided friction coefficients: slide, roll, spin', + ), + StructFieldDecl( + name='solmix', + type=ValueType(name='double'), + doc='solver mixing for contact pairs', + ), + StructFieldDecl( + name='solref', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(2,), + ), + doc='solver reference', + ), + StructFieldDecl( + name='solimp', + type=ArrayType( + inner_type=ValueType(name='mjtNum'), + extents=(5,), + ), + doc='solver impedance', + ), + StructFieldDecl( + name='margin', + type=ValueType(name='double'), + doc='margin for contact detection', + ), + StructFieldDecl( + name='gap', + type=ValueType(name='double'), + doc='include in solver if distspec; } @@ -60,7 +60,7 @@ mjSpec* mjs_createSpec() { // copy model -mjSpec* mjs_copySpec(const mjSpec* s) { +mjSpec* mj_copySpec(const mjSpec* s) { mjCModel* modelC = new mjCModel(*static_cast(s->element)); return &modelC->spec; } @@ -68,7 +68,7 @@ mjSpec* mjs_copySpec(const mjSpec* s) { // copy back model -void mjs_copyBack(mjSpec* s, const mjModel* m) { +void mj_copyBack(mjSpec* s, const mjModel* m) { mjCModel* modelC = static_cast(s->element); modelC->CopyBack(m); } @@ -76,7 +76,7 @@ void mjs_copyBack(mjSpec* s, const mjModel* m) { // compile model -mjModel* mjs_compile(mjSpec* s, const mjVFS* vfs) { +mjModel* mj_compile(mjSpec* s, const mjVFS* vfs) { mjCModel* modelC = static_cast(s->element); return modelC->Compile(vfs); } @@ -84,7 +84,7 @@ mjModel* mjs_compile(mjSpec* s, const mjVFS* vfs) { // recompile spec into existing model and data while preserving the state -void mjs_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d) { +void mj_recompile(mjSpec* s, const mjVFS* vfs, mjModel* m, mjData* d) { mjCModel* modelC = static_cast(s->element); modelC->SaveState(d); modelC->Compile(vfs, &m); @@ -143,7 +143,7 @@ int mjs_isWarning(mjSpec* s) { // delete model -void mjs_deleteSpec(mjSpec* s) { +void mj_deleteSpec(mjSpec* s) { mjCModel* model = static_cast(s->element); delete model; } @@ -151,7 +151,7 @@ void mjs_deleteSpec(mjSpec* s) { // delete object, it will call the appropriate destructor since ~mjCBase is virtual -void mjs_delete(mjElement* element) { +void mjs_delete(mjsElement* element) { mjCBase* object = static_cast(element); delete object; } @@ -430,7 +430,7 @@ mjsKey* mjs_addKey(mjSpec* s) { mjsPlugin* mjs_addPlugin(mjSpec* s) { mjCModel* modelC = static_cast(s->element); mjCPlugin* plugin = modelC->AddPlugin(); - plugin->spec.instance = static_cast(plugin); + plugin->spec.instance = static_cast(plugin); return &plugin->spec; } @@ -458,7 +458,7 @@ mjSpec* mjs_getSpec(mjsBody* body) { // get default -mjsDefault* mjs_getDefault(mjElement* element) { +mjsDefault* mjs_getDefault(mjsElement* element) { return &(static_cast(element)->def->spec); } @@ -530,7 +530,7 @@ mjsFrame* mjs_findFrame(mjSpec* s, const char* name) { // set frame -void mjs_setFrame(mjElement* dest, mjsFrame* frame) { +void mjs_setFrame(mjsElement* dest, mjsFrame* frame) { if (!frame) { return; } @@ -550,14 +550,14 @@ const char* mjs_resolveOrientation(double quat[4], mjtByte degree, const char* s // get id -int mjs_getId(mjElement* element) { +int mjs_getId(mjsElement* element) { return static_cast(element)->id; } // set default -void mjs_setDefault(mjElement* element, mjsDefault* defspec) { +void mjs_setDefault(mjsElement* element, mjsDefault* defspec) { mjCBase* baseC = static_cast(element); baseC->def = static_cast(defspec->element); } @@ -565,7 +565,7 @@ void mjs_setDefault(mjElement* element, mjsDefault* defspec) { // return first child of selected type -mjElement* mjs_firstChild(mjsBody* body, mjtObj type) { +mjsElement* mjs_firstChild(mjsBody* body, mjtObj type) { mjCBody* bodyC = static_cast(body->element); return bodyC->NextChild(NULL, type); } @@ -573,7 +573,7 @@ mjElement* mjs_firstChild(mjsBody* body, mjtObj type) { // return body's next child; return NULL if child is last -mjElement* mjs_nextChild(mjsBody* body, mjElement* child) { +mjsElement* mjs_nextChild(mjsBody* body, mjsElement* child) { mjCBody* bodyC = static_cast(body->element); return bodyC->NextChild(child); } diff --git a/src/user/user_api.h b/src/user/user_api.h index 1908791f..d7c03f0f 100644 --- a/src/user/user_api.h +++ b/src/user/user_api.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -33,749 +34,19 @@ extern "C" { #define mjNAN NAN // used to mark undefined fields - -//---------------------------------- C/C++ handles to strings and arrays --------------------------- - -#ifdef __cplusplus - // C++, defined to be compatible with corresponding std types - using mjString = std::string; - using mjStringVec = std::vector; - using mjIntVec = std::vector; - using mjIntVecVec = std::vector>; - using mjFloatVec = std::vector; - using mjFloatVecVec = std::vector>; - using mjDoubleVec = std::vector; -#else - // C, opaque pointers - typedef struct mjString_ mjString; - typedef struct mjStringVec_ mjStringVec; - typedef struct mjIntVec_ mjIntVec; - typedef struct mjIntVecVec_ mjIntVecVec; - typedef struct mjFloatVec_ mjFloatVec; - typedef struct mjFloatVecVec_ mjFloatVecVec; - typedef struct mjDoubleVec_ mjDoubleVec; -#endif - - -//---------------------------------- enum types (mjt) ---------------------------------------------- - -typedef enum mjtGeomInertia_ { // type of inertia inference - mjINERTIA_VOLUME, // mass distributed in the volume - mjINERTIA_SHELL, // mass distributed on the surface -} mjtGeomInertia; - - -typedef enum mjtBuiltin_ { // type of built-in procedural texture - mjBUILTIN_NONE = 0, // no built-in texture - mjBUILTIN_GRADIENT, // gradient: rgb1->rgb2 - mjBUILTIN_CHECKER, // checker pattern: rgb1, rgb2 - mjBUILTIN_FLAT // 2d: rgb1; cube: rgb1-up, rgb2-side, rgb3-down -} mjtBuiltin; - - -typedef enum mjtMark_ { // mark type for procedural textures - mjMARK_NONE = 0, // no mark - mjMARK_EDGE, // edges - mjMARK_CROSS, // cross - mjMARK_RANDOM // random dots -} mjtMark; - - -typedef enum mjtLimited_ { // type of limit specification - mjLIMITED_FALSE = 0, // not limited - mjLIMITED_TRUE, // limited - mjLIMITED_AUTO, // limited inferred from presence of range -} mjtLimited; - - -typedef enum mjtInertiaFromGeom_ { // whether to infer body inertias from child geoms - mjINERTIAFROMGEOM_FALSE = 0, // do not use; inertial element required - mjINERTIAFROMGEOM_TRUE, // always use; overwrite inertial element - mjINERTIAFROMGEOM_AUTO // use only if inertial element is missing -} mjtInertiaFromGeom; - - -typedef enum mjtOrientation_ { // type of orientation specifier - mjORIENTATION_QUAT = 0, // quaternion - mjORIENTATION_AXISANGLE, // axis and angle - mjORIENTATION_XYAXES, // x and y axes - mjORIENTATION_ZAXIS, // z axis (minimal rotation) - mjORIENTATION_EULER, // Euler angles -} mjtOrientation; - - -//---------------------------------- attribute structs (mjs) --------------------------------------- - -typedef struct mjElement_ { // element type, do not modify - mjtObj elemtype; // element type -} mjElement; - - -typedef struct mjSpec_ { // model specification - mjElement* element; // element type - mjString* modelname; // model name - - // compiler settings - mjtByte autolimits; // infer "limited" attribute based on range - double boundmass; // enforce minimum body mass - double boundinertia; // enforce minimum body diagonal inertia - double settotalmass; // rescale masses and inertias; <=0: ignore - mjtByte balanceinertia; // automatically impose A + B >= C rule - mjtByte strippath; // automatically strip paths from mesh files - mjtByte fitaabb; // meshfit to aabb instead of inertia box - mjtByte degree; // angles in radians or degrees - char euler[3]; // sequence for euler rotations - mjString* meshdir; // mesh and hfield directory - mjString* texturedir; // texture directory - mjtByte discardvisual; // discard visual geoms in parser - mjtByte convexhull; // compute mesh convex hulls - mjtByte usethread; // use multiple threads to speed up compiler - mjtByte fusestatic; // fuse static bodies with parent - int inertiafromgeom; // use geom inertias (mjtInertiaFromGeom) - int inertiagrouprange[2]; // range of geom groups used to compute inertia - mjtByte exactmeshinertia; // if false, use old formula - mjLROpt LRopt; // options for lengthrange computation - - // engine data - mjOption option; // physics options - mjVisual visual; // visual options - mjStatistic stat; // statistics override (if defined) - - // sizes - size_t memory; // number of bytes in arena+stack memory - int nemax; // max number of equality constraints - int nuserdata; // number of mjtNums in userdata - int nuser_body; // number of mjtNums in body_user - int nuser_jnt; // number of mjtNums in jnt_user - int nuser_geom; // number of mjtNums in geom_user - int nuser_site; // number of mjtNums in site_user - int nuser_cam; // number of mjtNums in cam_user - int nuser_tendon; // number of mjtNums in tendon_user - int nuser_actuator; // number of mjtNums in actuator_user - int nuser_sensor; // number of mjtNums in sensor_user - int nkey; // number of keyframes - int njmax; // (deprecated) max number of constraints - int nconmax; // (deprecated) max number of detected contacts - size_t nstack; // (deprecated) number of mjtNums in mjData stack - - // global data - mjString* comment; // comment at top of XML - mjString* modelfiledir; // path to model file - - // other - mjtByte hasImplicitPluginElem; // already encountered an implicit plugin sensor/actuator -} mjSpec; - - -typedef struct mjsOrientation_ { // alternative orientation specifiers - mjtOrientation type; // active orientation specifier - double axisangle[4]; // axis and angle - double xyaxes[6]; // x and y axes - double zaxis[3]; // z axis (minimal rotation) - double euler[3]; // Euler angles -} mjsOrientation; - - -typedef struct mjsPlugin_ { // plugin specification - mjElement* instance; // element type - mjString* name; // name - mjString* instance_name; // instance name - int plugin_slot; // global registered slot number of the plugin - mjtByte active; // is the plugin active - mjString* info; // message appended to compiler errors -} mjsPlugin; - - -typedef struct mjsBody_ { // body specification - mjElement* element; // element type - mjString* name; // name - mjString* childclass; // childclass name - - // body frame - double pos[3]; // frame position - double quat[4]; // frame orientation - mjsOrientation alt; // frame alternative orientation - - // inertial frame - double mass; // mass - double ipos[3]; // inertial frame position - double iquat[4]; // inertial frame orientation - double inertia[3]; // diagonal inertia (in i-frame) - mjsOrientation ialt; // inertial frame alternative orientation - double fullinertia[6]; // non-axis-aligned inertia matrix - - // other - mjtByte mocap; // is this a mocap body - double gravcomp; // gravity compensation - mjDoubleVec* userdata; // user data - mjtByte explicitinertial; // whether to save the body with explicit inertial clause - mjsPlugin plugin; // passive force plugin - mjString* info; // message appended to compiler errors -} mjsBody; - - -typedef struct mjsFrame_ { // frame specification - mjElement* element; // element type - mjString* name; // name - mjString* childclass; // childclass name - double pos[3]; // position - double quat[4]; // orientation - mjsOrientation alt; // alternative orientation - mjString* info; // message appended to compiler errors -} mjsFrame; - - -typedef struct mjsJoint_ { // joint specification - mjElement* element; // element type - mjString* name; // name - mjString* classname; // class name - mjtJoint type; // joint type - - // kinematics - double pos[3]; // anchor position - double axis[3]; // joint axis - double ref; // value at reference configuration: qpos0 - - // stiffness - double stiffness; // stiffness coefficient - double springref; // spring reference value: qpos_spring - double springdamper[2]; // timeconst, dampratio - - // limits - int limited; // does joint have limits (mjtLimited) - double range[2]; // joint limits - double margin; // margin value for joint limit detection - mjtNum solref_limit[mjNREF]; // solver reference: joint limits - mjtNum solimp_limit[mjNIMP]; // solver impedance: joint limits - int actfrclimited; // are actuator forces on joint limited (mjtLimited) - double actfrcrange[2]; // actuator force limits - - // dof properties - double armature; // armature inertia (mass for slider) - double damping; // damping coefficient - double frictionloss; // friction loss - mjtNum solref_friction[mjNREF]; // solver reference: dof friction - mjtNum solimp_friction[mjNIMP]; // solver impedance: dof friction - - // other - int group; // group - mjtByte actgravcomp; // is gravcomp force applied via actuators - mjDoubleVec* userdata; // user data - mjString* info; // message appended to compiler errors -} mjsJoint; - - -typedef struct mjsGeom_ { // geom specification - mjElement* element; // element type - mjString* name; // name - mjString* classname; // classname - mjtGeom type; // geom type - - // frame, size - double pos[3]; // position - double quat[4]; // orientation - mjsOrientation alt; // alternative orientation - double fromto[6]; // alternative for capsule, cylinder, box, ellipsoid - double size[3]; // type-specific size - - // contact related - int contype; // contact type - int conaffinity; // contact affinity - int condim; // contact dimensionality - int priority; // contact priority - double friction[3]; // one-sided friction coefficients: slide, roll, spin - double solmix; // solver mixing for contact pairs - mjtNum solref[mjNREF]; // solver reference - mjtNum solimp[mjNIMP]; // solver impedance - double margin; // margin for contact detection - double gap; // include in solver if dist < margin-gap - - // inertia inference - double mass; // used to compute density - double density; // used to compute mass and inertia from volume or surface - mjtGeomInertia typeinertia; // selects between surface and volume inertia - - // fluid forces - mjtNum fluid_ellipsoid; // whether ellipsoid-fluid model is active - mjtNum fluid_coefs[5]; // ellipsoid-fluid interaction coefs - - // visual - mjString* material; // name of material - float rgba[4]; // rgba when material is omitted - int group; // group - - // other - mjString* hfieldname; // heightfield attached to geom - mjString* meshname; // mesh attached to geom - double fitscale; // scale mesh uniformly - mjDoubleVec* userdata; // user data - mjsPlugin plugin; // sdf plugin - mjString* info; // message appended to compiler errors -} mjsGeom; - - -typedef struct mjsSite_ { // site specification - mjElement* element; // element type - mjString* name; // name - mjString* classname; // class name - - // frame, size - double pos[3]; // position - double quat[4]; // orientation - mjsOrientation alt; // alternative orientation - double fromto[6]; // alternative for capsule, cylinder, box, ellipsoid - double size[3]; // geom size - - // visual - mjtGeom type; // geom type - mjString* material; // name of material - int group; // group - float rgba[4]; // rgba when material is omitted - - // other - mjDoubleVec* userdata; // user data - mjString* info; // message appended to compiler errors -} mjsSite; - - -typedef struct mjsCamera_ { // camera specification - mjElement* element; // element type - mjString* name; // name - mjString* classname; // class name - - // extrinsics - double pos[3]; // position - double quat[4]; // orientation - mjsOrientation alt; // alternative orientation - mjtCamLight mode; // tracking mode - mjString* targetbody; // target body for tracking/targeting - - // intrinsics - double fovy; // y-field of view - double ipd; // inter-pupilary distance - float intrinsic[4]; // camera intrinsics (length) - float sensor_size[2]; // sensor size (length) - float resolution[2]; // resolution (pixel) - float focal_length[2]; // focal length (length) - float focal_pixel[2]; // focal length (pixel) - float principal_length[2]; // principal point (length) - float principal_pixel[2]; // principal point (pixel) - - // other - mjDoubleVec* userdata; // user data - mjString* info; // message appended to compiler errors -} mjsCamera; - - -typedef struct mjsLight_ { // light specification - mjElement* element; // element type - mjString* name; // name - mjString* classname; // class name - - // frame - double pos[3]; // position - double dir[3]; // direction - mjtCamLight mode; // tracking mode - mjString* targetbody; // target body for targeting - - // intrinsics - mjtByte active; // is light active - mjtByte directional; // is light directional or spot - mjtByte castshadow; // does light cast shadows - double bulbradius; // bulb radius, for soft shadows - float attenuation[3]; // OpenGL attenuation (quadratic model) - float cutoff; // OpenGL cutoff - float exponent; // OpenGL exponent - float ambient[3]; // ambient color - float diffuse[3]; // diffuse color - float specular[3]; // specular color - - // other - mjString* info; // message appended to compiler errorsx -} mjsLight; - - -typedef struct mjsFlex_ { - mjElement* element; // element type - mjString* name; // name - mjString* classname; // class name - - // contact properties - int contype; // contact type - int conaffinity; // contact affinity - int condim; // contact dimensionality - int priority; // contact priority - double friction[3]; // one-sided friction coefficients: slide, roll, spin - double solmix; // solver mixing for contact pairs - mjtNum solref[mjNREF]; // solver reference - mjtNum solimp[mjNIMP]; // solver impedance - double margin; // margin for contact detection - double gap; // include in solver if dist #include "user/user_composite.h" #include diff --git a/src/user/user_composite.h b/src/user/user_composite.h index bbc49a72..88bccedc 100644 --- a/src/user/user_composite.h +++ b/src/user/user_composite.h @@ -20,7 +20,7 @@ #include #include -#include "user/user_api.h" +#include #include "user/user_model.h" #include "user/user_objects.h" diff --git a/src/user/user_flexcomp.cc b/src/user/user_flexcomp.cc index 066e74a7..663c8b6e 100644 --- a/src/user/user_flexcomp.cc +++ b/src/user/user_flexcomp.cc @@ -34,7 +34,7 @@ #include "engine/engine_util_misc.h" #include "engine/engine_util_spatial.h" #include "user/user_flexcomp.h" -#include "user/user_api.h" +#include #include "user/user_model.h" #include "user/user_objects.h" #include "user/user_util.h" @@ -443,7 +443,7 @@ bool mjCFlexcomp::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) { if (plugin.active) { mjsPlugin* pplugin = &body->plugin; pplugin->active = true; - pplugin->instance = static_cast(plugin.instance); + pplugin->instance = static_cast(plugin.instance); mjs_setString(pplugin->name, mjs_getString(plugin.name)); mjs_setString(pplugin->instance_name, plugin_instance_name.c_str()); } @@ -507,7 +507,7 @@ bool mjCFlexcomp::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) { if (plugin.active) { mjsPlugin* pplugin = &pb->plugin; pplugin->active = true; - pplugin->instance = static_cast(plugin.instance); + pplugin->instance = static_cast(plugin.instance); mjs_setString(pplugin->name, mjs_getString(plugin.name)); mjs_setString(pplugin->instance_name, plugin_instance_name.c_str()); } diff --git a/src/user/user_flexcomp.h b/src/user/user_flexcomp.h index 5d0ca04f..a22780aa 100644 --- a/src/user/user_flexcomp.h +++ b/src/user/user_flexcomp.h @@ -19,7 +19,7 @@ #include #include -#include "user/user_api.h" +#include #include "user/user_model.h" #include "user/user_objects.h" diff --git a/src/user/user_init.c b/src/user/user_init.c index 6a6c8d15..864c7ec1 100644 --- a/src/user/user_init.c +++ b/src/user/user_init.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "engine/engine_io.h" #include "user/user_api.h" diff --git a/src/user/user_mesh.cc b/src/user/user_mesh.cc index 79a1d1d0..61e8265f 100644 --- a/src/user/user_mesh.cc +++ b/src/user/user_mesh.cc @@ -27,6 +27,7 @@ #include #include +#include #include "user/user_api.h" #ifdef MUJOCO_TINYOBJLOADER_IMPL @@ -196,7 +197,7 @@ mjCMesh& mjCMesh::operator=(const mjCMesh& other) { void mjCMesh::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.file = &spec_file_; @@ -1983,7 +1984,7 @@ mjCSkin& mjCSkin::operator=(const mjCSkin& other) { void mjCSkin::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.file = &spec_file_; @@ -2395,7 +2396,7 @@ mjCFlex& mjCFlex::operator=(const mjCFlex& other) { void mjCFlex::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.material = &spec_material_; diff --git a/src/user/user_model.cc b/src/user/user_model.cc index 0a4f6f44..3a48ace2 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -416,7 +417,7 @@ void mjCModel::CreateObjectLists() { void mjCModel::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.comment = &spec_comment_; spec.modelfiledir = &spec_modelfiledir_; spec.modelname = &spec_modelname_; diff --git a/src/user/user_model.h b/src/user/user_model.h index 95939964..5d2ee204 100644 --- a/src/user/user_model.h +++ b/src/user/user_model.h @@ -27,13 +27,13 @@ #include #include #include -#include "user/user_api.h" +#include #include "user/user_objects.h" typedef std::map > mjKeyMap; typedef std::array mjListKeyMap; -class mjCModel_ : public mjElement { +class mjCModel_ : public mjsElement { public: // attach namespaces std::string prefix; diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc index 6ff8c13c..a1d9329b 100644 --- a/src/user/user_objects.cc +++ b/src/user/user_objects.cc @@ -45,6 +45,7 @@ #include "engine/engine_util_misc.h" #include "engine/engine_util_solve.h" #include "engine/engine_util_spatial.h" +#include #include "user/user_api.h" #include "user/user_cache.h" #include "user/user_model.h" @@ -640,7 +641,7 @@ void mjCDef::PointToLocal() { equality_.PointToLocal(); tendon_.PointToLocal(); actuator_.PointToLocal(); - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.joint = &joint_.spec; spec.geom = &geom_.spec; @@ -949,7 +950,7 @@ mjCBody& mjCBody::operator-=(const mjCBody& subtree) { void mjCBody::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.childclass = &classname; spec.userdata = &spec_userdata_; @@ -1256,7 +1257,7 @@ mjCBase* mjCBody::FindObject(mjtObj type, string _name, bool recursive) { template -static mjElement* GetNext(std::vector& list, mjElement* child) { +static mjsElement* GetNext(std::vector& list, mjsElement* child) { for (unsigned int i = 0; i < list.size()-1; i++) { if (list[i]->spec.element == child) { return list[i+1]->spec.element; @@ -1268,7 +1269,7 @@ static mjElement* GetNext(std::vector& list, mjElement* child) { // get next child of given type -mjElement* mjCBody::NextChild(mjElement* child, mjtObj type) { +mjsElement* mjCBody::NextChild(mjsElement* child, mjtObj type) { if (type == mjOBJ_UNKNOWN) { if (!child) { throw mjCError(this, "child type must be specified if no child element is given"); @@ -1669,7 +1670,7 @@ void mjCFrame::SetParent(mjCBody* _body) { void mjCFrame::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.childclass = &classname; spec.info = &info; @@ -1765,7 +1766,7 @@ bool mjCJoint::is_actfrclimited() const { return islimited(actfrclimited, actfrc void mjCJoint::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.userdata = &spec_userdata_; @@ -1965,7 +1966,7 @@ mjCGeom& mjCGeom::operator=(const mjCGeom& other) { // to be called after any default copy constructor void mjCGeom::PointToLocal(void) { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.info = &info; spec.classname = &classname; @@ -2598,7 +2599,7 @@ mjCSite& mjCSite::operator=(const mjCSite& other) { void mjCSite::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.info = &info; spec.classname = &classname; @@ -2750,7 +2751,7 @@ mjCCamera& mjCCamera::operator=(const mjCCamera& other) { void mjCCamera::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.userdata = &spec_userdata_; @@ -2901,7 +2902,7 @@ mjCLight& mjCLight::operator=(const mjCLight& other) { void mjCLight::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.targetbody = &spec_targetbody_; @@ -3001,7 +3002,7 @@ mjCHField& mjCHField::operator=(const mjCHField& other) { void mjCHField::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.file = &spec_file_; spec.content_type = &spec_content_type_; @@ -3230,7 +3231,7 @@ mjCTexture& mjCTexture::operator=(const mjCTexture& other) { void mjCTexture::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.file = &spec_file_; @@ -3941,7 +3942,7 @@ mjCMaterial& mjCMaterial::operator=(const mjCMaterial& other) { void mjCMaterial::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.texture = &spec_texture_; @@ -4031,7 +4032,7 @@ mjCPair& mjCPair::operator=(const mjCPair& other) { void mjCPair::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.geomname1 = &spec_geomname1_; @@ -4256,7 +4257,7 @@ mjCBodyPair& mjCBodyPair::operator=(const mjCBodyPair& other) { void mjCBodyPair::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.bodyname1 = &spec_bodyname1_; spec.bodyname2 = &spec_bodyname2_; @@ -4390,7 +4391,7 @@ mjCEquality& mjCEquality::operator=(const mjCEquality& other) { void mjCEquality::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.name1 = &spec_name1_; @@ -4559,7 +4560,7 @@ bool mjCTendon::is_limited() const { return islimited(limited, range); } void mjCTendon::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.material = &spec_material_; @@ -4891,7 +4892,7 @@ mjCWrap& mjCWrap::operator=(const mjCWrap& other) { void mjCWrap::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.info = &info; } @@ -5040,7 +5041,7 @@ bool mjCActuator::is_actlimited() const { return islimited(actlimited, actrange) void mjCActuator::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.userdata = &spec_userdata_; @@ -5363,7 +5364,7 @@ mjCSensor& mjCSensor::operator=(const mjCSensor& other) { void mjCSensor::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.classname = &classname; spec.userdata = &spec_userdata_; @@ -5866,7 +5867,7 @@ mjCNumeric& mjCNumeric::operator=(const mjCNumeric& other) { void mjCNumeric::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.data = &spec_data_; spec.info = &info; @@ -5955,7 +5956,7 @@ mjCText& mjCText::operator=(const mjCText& other) { void mjCText::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.data = &spec_data_; spec.info = &info; @@ -6035,7 +6036,7 @@ mjCTuple& mjCTuple::operator=(const mjCTuple& other) { void mjCTuple::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.objtype = (mjIntVec*)&spec_objtype_; spec.objname = &spec_objname_; @@ -6170,7 +6171,7 @@ mjCKey& mjCKey::operator=(const mjCKey& other) { void mjCKey::PointToLocal() { - spec.element = static_cast(this); + spec.element = static_cast(this); spec.name = &name; spec.qpos = &spec_qpos_; spec.qvel = &spec_qvel_; diff --git a/src/user/user_objects.h b/src/user/user_objects.h index e3548686..cd79226a 100644 --- a/src/user/user_objects.h +++ b/src/user/user_objects.h @@ -26,7 +26,7 @@ #include #include #include -#include "user/user_api.h" +#include #include "user/user_cache.h" #include "user/user_util.h" @@ -168,7 +168,7 @@ class mjCBoundingVolumeHierarchy : public mjCBoundingVolumeHierarchy_ { //------------------------- class mjCBase ---------------------------------------------------------- // Generic functionality for all derived classes -class mjCBase_ : public mjElement { +class mjCBase_ : public mjsElement { public: int id; // object id std::string name; // object name @@ -309,7 +309,7 @@ class mjCBody : public mjCBody_, private mjsBody { const std::vector& get_userdata() { return userdata_; } // get next child of given type - mjElement* NextChild(mjElement* child, mjtObj type = mjOBJ_UNKNOWN); + mjsElement* NextChild(mjsElement* child, mjtObj type = mjOBJ_UNKNOWN); private: mjCBody(const mjCBody& other, mjCModel* _model); // copy constructor @@ -1624,7 +1624,7 @@ class mjCKey : public mjCKey_, private mjsKey { //------------------------- class mjCDef ----------------------------------------------------------- // Describes one set of defaults -class mjCDef : public mjElement { +class mjCDef : public mjsElement { friend class mjXWriter; public: diff --git a/src/xml/xml.cc b/src/xml/xml.cc index ad1cf06f..0e5fd090 100644 --- a/src/xml/xml.cc +++ b/src/xml/xml.cc @@ -35,7 +35,7 @@ #include "cc/array_safety.h" #include "engine/engine_crossplatform.h" #include "engine/engine_resource.h" -#include "user/user_api.h" +#include #include "user/user_util.h" #include "user/user_vfs.h" #include "xml/xml_native_reader.h" @@ -345,7 +345,7 @@ mjSpec* mjParseXML(const char* filename, const mjVFS* vfs, } // create model, set filedir - spec = mjs_createSpec(); + spec = mj_makeSpec(); const char* dir; int ndir = 0; mju_getResourceDir(resource, &dir, &ndir); @@ -394,7 +394,7 @@ mjSpec* mjParseXML(const char* filename, const mjVFS* vfs, // catch known errors catch (mjXError err) { mjCopyError(error, err.message, error_sz); - mjs_deleteSpec(spec); + mj_deleteSpec(spec); return nullptr; } diff --git a/src/xml/xml.h b/src/xml/xml.h index 2fdd3acc..dadc2451 100644 --- a/src/xml/xml.h +++ b/src/xml/xml.h @@ -19,7 +19,7 @@ #include #include -#include "user/user_api.h" +#include // Top level API diff --git a/src/xml/xml_api.cc b/src/xml/xml_api.cc index 23af4c81..65a2198b 100644 --- a/src/xml/xml_api.cc +++ b/src/xml/xml_api.cc @@ -28,7 +28,7 @@ #include #include "engine/engine_io.h" #include "engine/engine_resource.h" -#include "user/user_api.h" +#include #include "user/user_vfs.h" #include "xml/xml.h" #include "xml/xml_native_reader.h" @@ -59,7 +59,7 @@ std::optional GlobalModel::ToXML(const mjModel* m, char* error, mjCopyError(error, "No XML model loaded", error_sz); return std::nullopt; } - mjs_copyBack(spec_, m); + mj_copyBack(spec_, m); std::string result = mjWriteXML(spec_, error, error_sz); if (result.empty()) { return std::nullopt; @@ -70,7 +70,7 @@ std::optional GlobalModel::ToXML(const mjModel* m, char* error, void GlobalModel::Set(mjSpec* spec) { std::lock_guard lock(*mutex_); if (spec_ != nullptr) { - mjs_deleteSpec(spec_); + mj_deleteSpec(spec_); } spec_ = spec; } @@ -96,13 +96,13 @@ mjModel* mj_loadXML(const char* filename, const mjVFS* vfs, // parse new model std::unique_ptr> spec( mjParseXML(filename, vfs, error, error_sz), - [](mjSpec* s) { mjs_deleteSpec(s); }); + [](mjSpec* s) { mj_deleteSpec(s); }); if (!spec) { return nullptr; } // compile new model - mjModel* m = mjs_compile(spec.get(), vfs); + mjModel* m = mj_compile(spec.get(), vfs); if (!m) { mjCopyError(error, mjs_getError(spec.get()), error_sz); return nullptr; @@ -248,7 +248,7 @@ int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int er std::string error_msg = "Output string too short, should be at least " + std::to_string(result.size()+1); mjCopyError(error, error_msg.c_str(), error_sz); - return 0; + return result.size(); } if (result.empty()) { return 0; @@ -256,6 +256,6 @@ int mj_saveXMLString(const mjSpec* s, char* xml, int xml_sz, char* error, int er result.copy(xml, xml_sz); xml[result.size()] = 0; - return 1; + return 0; } diff --git a/src/xml/xml_api.h b/src/xml/xml_api.h index 046e8410..fe4d0f18 100644 --- a/src/xml/xml_api.h +++ b/src/xml/xml_api.h @@ -17,7 +17,7 @@ #include #include -#include "user/user_api.h" +#include #ifdef __cplusplus extern "C" { diff --git a/src/xml/xml_base.cc b/src/xml/xml_base.cc index bcefd583..4d809581 100644 --- a/src/xml/xml_base.cc +++ b/src/xml/xml_base.cc @@ -21,9 +21,7 @@ #include #include -#include "user/user_api.h" -#include "user/user_model.h" -#include "user/user_objects.h" +#include #include "xml/xml_util.h" #include "tinyxml2.h" diff --git a/src/xml/xml_base.h b/src/xml/xml_base.h index 7df966cc..ec3fd74c 100644 --- a/src/xml/xml_base.h +++ b/src/xml/xml_base.h @@ -19,7 +19,7 @@ #include #include "tinyxml2.h" -#include "user/user_api.h" +#include #include "xml/xml_util.h" diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 786e54ae..59aa680e 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -36,7 +36,7 @@ #include "engine/engine_plugin.h" #include "engine/engine_util_errmem.h" #include "engine/engine_util_misc.h" -#include "user/user_api.h" +#include #include "user/user_composite.h" #include "user/user_flexcomp.h" #include "user/user_util.h" diff --git a/src/xml/xml_native_reader.h b/src/xml/xml_native_reader.h index e0f857db..aa20841a 100644 --- a/src/xml/xml_native_reader.h +++ b/src/xml/xml_native_reader.h @@ -21,7 +21,7 @@ #include "tinyxml2.h" #include -#include "user/user_api.h" +#include #include "xml/xml_base.h" #include "xml/xml_util.h" diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index c9012bdd..a9cda59a 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -22,11 +22,12 @@ #include #include +#include +#include #include "engine/engine_io.h" #include "engine/engine_plugin.h" #include "engine/engine_util_errmem.h" #include "engine/engine_util_misc.h" -#include "user/user_api.h" #include "user/user_model.h" #include "user/user_objects.h" #include "user/user_util.h" diff --git a/src/xml/xml_native_writer.h b/src/xml/xml_native_writer.h index 6f5f0812..c6791acd 100644 --- a/src/xml/xml_native_writer.h +++ b/src/xml/xml_native_writer.h @@ -18,7 +18,7 @@ #include #include -#include "user/user_api.h" +#include #include "user/user_objects.h" #include "xml/xml_base.h" #include "tinyxml2.h" diff --git a/src/xml/xml_urdf.cc b/src/xml/xml_urdf.cc index 8001d93a..cf3b841d 100644 --- a/src/xml/xml_urdf.cc +++ b/src/xml/xml_urdf.cc @@ -20,6 +20,7 @@ #include #include +#include #include "user/user_api.h" #include "user/user_util.h" #include "xml/xml_native_reader.h" diff --git a/src/xml/xml_urdf.h b/src/xml/xml_urdf.h index d2efa317..b549ac6e 100644 --- a/src/xml/xml_urdf.h +++ b/src/xml/xml_urdf.h @@ -19,7 +19,7 @@ #include #include -#include "user/user_api.h" +#include #include "xml/xml_base.h" #include "tinyxml2.h" diff --git a/test/user/user_api_test.cc b/test/user/user_api_test.cc index 2bc661bc..c0120a29 100644 --- a/test/user/user_api_test.cc +++ b/test/user/user_api_test.cc @@ -24,7 +24,7 @@ #include #include #include -#include "src/user/user_api.h" +#include #include "src/xml/xml_api.h" #include "src/xml/xml_numeric_format.h" #include "test/fixture.h" @@ -39,7 +39,7 @@ using ::testing::NotNull; // -------------------------- test model manipulation ------------------------- TEST_F(MujocoTest, GetSetData) { - mjSpec* spec = mjs_createSpec(); + mjSpec* spec = mj_makeSpec(); mjsBody* world = mjs_findBody(spec, "world"); mjsBody* body = mjs_addBody(world, 0); mjsSite* site = mjs_addSite(body, 0); @@ -60,11 +60,11 @@ TEST_F(MujocoTest, GetSetData) { EXPECT_EQ(vec[i], i); } - mjs_deleteSpec(spec); + mj_deleteSpec(spec); } TEST_F(MujocoTest, TreeTraversal) { - mjSpec* spec = mjs_createSpec(); + mjSpec* spec = mj_makeSpec(); mjsBody* world = mjs_findBody(spec, "world"); mjsBody* body = mjs_addBody(world, 0); @@ -75,15 +75,15 @@ TEST_F(MujocoTest, TreeTraversal) { mjsSite* site3 = mjs_addSite(body, 0); mjsGeom* geom3 = mjs_addGeom(body, 0); - mjElement* t_el1 = mjs_firstChild(body, mjOBJ_TENDON); - mjElement* s_el1 = mjs_firstChild(body, mjOBJ_SITE); - mjElement* s_el2 = mjs_nextChild(body, s_el1); - mjElement* s_el3 = mjs_nextChild(body, s_el2); - mjElement* s_el4 = mjs_nextChild(body, s_el3); - mjElement* g_el1 = mjs_firstChild(body, mjOBJ_GEOM); - mjElement* g_el2 = mjs_nextChild(body, g_el1); - mjElement* g_el3 = mjs_nextChild(body, g_el2); - mjElement* g_el4 = mjs_nextChild(body, g_el3); + mjsElement* t_el1 = mjs_firstChild(body, mjOBJ_TENDON); + mjsElement* s_el1 = mjs_firstChild(body, mjOBJ_SITE); + mjsElement* s_el2 = mjs_nextChild(body, s_el1); + mjsElement* s_el3 = mjs_nextChild(body, s_el2); + mjsElement* s_el4 = mjs_nextChild(body, s_el3); + mjsElement* g_el1 = mjs_firstChild(body, mjOBJ_GEOM); + mjsElement* g_el2 = mjs_nextChild(body, g_el1); + mjsElement* g_el3 = mjs_nextChild(body, g_el2); + mjsElement* g_el4 = mjs_nextChild(body, g_el3); EXPECT_EQ(t_el1, nullptr); EXPECT_EQ(s_el1, site1->element); @@ -95,7 +95,7 @@ TEST_F(MujocoTest, TreeTraversal) { EXPECT_EQ(g_el4, nullptr); EXPECT_EQ(s_el4, nullptr); - mjs_deleteSpec(spec); + mj_deleteSpec(spec); } // ------------------- test recompilation multiple files ----------------------- @@ -131,16 +131,16 @@ TEST_F(PluginTest, RecompileCompare) { << "Failed to load " << xml << ": " << err.data(); // copy spec - mjSpec* s_copy = mjs_copySpec(s); + mjSpec* s_copy = mj_copySpec(s); // compile twice and compare - mjModel* m_old = mjs_compile(s, nullptr); + mjModel* m_old = mj_compile(s, nullptr); ASSERT_THAT(m_old, NotNull()) << "Failed to compile " << xml << ": " << mjs_getError(s); - mjModel* m_new = mjs_compile(s, nullptr); - mjModel* m_copy = mjs_compile(s_copy, nullptr); + mjModel* m_new = mj_compile(s, nullptr); + mjModel* m_copy = mj_compile(s_copy, nullptr); ASSERT_THAT(m_new, NotNull()) << "Failed to recompile " << xml << ": " << mjs_getError(s); @@ -158,8 +158,8 @@ TEST_F(PluginTest, RecompileCompare) { << "Different field: " << field << '\n'; // copy to a new spec, compile and compare - mjSpec* s_copy2 = mjs_copySpec(s); - mjModel* m_copy2 = mjs_compile(s_copy2, nullptr); + mjSpec* s_copy2 = mj_copySpec(s); + mjModel* m_copy2 = mj_compile(s_copy2, nullptr); ASSERT_THAT(m_copy2, NotNull()) << "Failed to compile " << xml << ": " << mjs_getError(s_copy2); @@ -170,9 +170,9 @@ TEST_F(PluginTest, RecompileCompare) { << "Different field: " << field << '\n'; // delete models - mjs_deleteSpec(s); - mjs_deleteSpec(s_copy); - mjs_deleteSpec(s_copy2); + mj_deleteSpec(s); + mj_deleteSpec(s_copy); + mj_deleteSpec(s_copy2); mj_deleteModel(m_old); mj_deleteModel(m_new); mj_deleteModel(m_copy); @@ -433,7 +433,7 @@ TEST_F(MujocoTest, AttachSame) { mjs_attachBody(frame, body, /*prefix=*/"attached-", /*suffix=*/"-1"), 0); // compile new model - mjModel* m_attached = mjs_compile(parent, 0); + mjModel* m_attached = mj_compile(parent, 0); EXPECT_THAT(m_attached, NotNull()); // check full name stored in mjModel @@ -450,7 +450,7 @@ TEST_F(MujocoTest, AttachSame) { << "Different field: " << field << '\n';; // destroy everything - mjs_deleteSpec(parent); + mj_deleteSpec(parent); mj_deleteModel(m_attached); mj_deleteModel(m_expected); } @@ -550,7 +550,7 @@ TEST_F(MujocoTest, AttachDifferent) { mjs_attachBody(frame, body, /*prefix=*/"attached-", /*suffix=*/"-1"), 0); // compile new model - mjModel* m_attached = mjs_compile(parent, 0); + mjModel* m_attached = mj_compile(parent, 0); EXPECT_THAT(m_attached, NotNull()); // check full name stored in mjModel @@ -567,8 +567,8 @@ TEST_F(MujocoTest, AttachDifferent) { << "Different field: " << field << '\n';; // destroy everything - mjs_deleteSpec(parent); - mjs_deleteSpec(child); + mj_deleteSpec(parent); + mj_deleteSpec(child); mj_deleteModel(m_attached); mj_deleteModel(m_expected); } @@ -662,7 +662,7 @@ TEST_F(MujocoTest, AttachFrame) { mjs_attachFrame(body, frame, /*prefix=*/"attached-", /*suffix=*/"-1"), 0); // compile new model - mjModel* m_attached = mjs_compile(parent, 0); + mjModel* m_attached = mj_compile(parent, 0); EXPECT_THAT(m_attached, NotNull()); // check full name stored in mjModel @@ -679,8 +679,8 @@ TEST_F(MujocoTest, AttachFrame) { << "Different field: " << field << '\n';; // destroy everything - mjs_deleteSpec(parent); - mjs_deleteSpec(child); + mj_deleteSpec(parent); + mj_deleteSpec(child); mj_deleteModel(m_attached); mj_deleteModel(m_expected); } @@ -715,7 +715,7 @@ void TestDetachBody(bool compile) { EXPECT_THAT(child, NotNull()) << er.data(); // compile model (for testing double compilation) - mjModel* m_child = compile ? mjs_compile(child, 0) : nullptr; + mjModel* m_child = compile ? mj_compile(child, 0) : nullptr; // get subtree mjsBody* body = mjs_findBody(child, "body"); @@ -725,7 +725,7 @@ void TestDetachBody(bool compile) { EXPECT_THAT(mjs_detachBody(child, body), 0); // compile new model - mjModel* m_detached = mjs_compile(child, 0); + mjModel* m_detached = mj_compile(child, 0); EXPECT_THAT(m_detached, NotNull()); // compare with expected XML @@ -736,7 +736,7 @@ void TestDetachBody(bool compile) { << "Different field: " << field << '\n'; // destroy everything - mjs_deleteSpec(child); + mj_deleteSpec(child); mj_deleteModel(m_detached); mj_deleteModel(m_expected); if (m_child) mj_deleteModel(m_child); @@ -791,7 +791,7 @@ TEST_F(MujocoTest, PreserveState) { EXPECT_THAT(spec, NotNull()) << er.data(); // compile models - mjModel* model = mjs_compile(spec, 0); + mjModel* model = mj_compile(spec, 0); EXPECT_THAT(model, NotNull()); mjModel* m_expected = LoadModelFromString(xml_expected, er.data(), er.size()); EXPECT_THAT(m_expected, NotNull()); @@ -834,7 +834,7 @@ TEST_F(MujocoTest, PreserveState) { joint->ref = d_expected->qpos[m_expected->nq-1]; // compile new model - mjs_recompile(spec, 0, model, data); + mj_recompile(spec, 0, model, data); EXPECT_THAT(model, NotNull()); // compare qpos @@ -861,7 +861,7 @@ TEST_F(MujocoTest, PreserveState) { // destroy everything mj_deleteData(data); mj_deleteData(d_expected); - mjs_deleteSpec(spec); + mj_deleteSpec(spec); mj_deleteModel(model); mj_deleteModel(m_expected); } diff --git a/test/xml/xml_api_test.cc b/test/xml/xml_api_test.cc index d11be846..8c5041a2 100644 --- a/test/xml/xml_api_test.cc +++ b/test/xml/xml_api_test.cc @@ -24,7 +24,7 @@ #include #include #include -#include "src/user/user_api.h" +#include #include "src/xml/xml_api.h" #include "test/fixture.h" @@ -119,15 +119,15 @@ TEST_F(MujocoTest, SaveXmlShortString) { mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); EXPECT_THAT(spec, NotNull()) << "Failed to parse spec: " << error.data(); - mjModel* model = mjs_compile(spec, 0); + mjModel* model = mj_compile(spec, 0); EXPECT_THAT(model, NotNull()) << "Failed to compile model: " << error.data(); std::array out; EXPECT_THAT(mj_saveXMLString(spec, out.data(), out.size(), - error.data(), error.size()), 0); + error.data(), error.size()), 272); EXPECT_STREQ(error.data(), "Output string too short, should be at least 273"); - mjs_deleteSpec(spec); + mj_deleteSpec(spec); mj_deleteModel(model); } @@ -136,16 +136,16 @@ TEST_F(MujocoTest, SaveXml) { mjSpec* spec = mj_parseXMLString(xml, 0, error.data(), error.size()); EXPECT_THAT(spec, NotNull()) << "Failed to parse spec: " << error.data(); - mjModel* model = mjs_compile(spec, 0); + mjModel* model = mj_compile(spec, 0); EXPECT_THAT(model, NotNull()) << "Failed to compile model: " << error.data(); std::array out; EXPECT_THAT(mj_saveXMLString(spec, out.data(), out.size(), error.data(), - error.size()), 1) << error.data(); + error.size()), 0) << error.data(); mjSpec* saved_spec = mj_parseXMLString(xml, 0, error.data(), error.size()); EXPECT_THAT(saved_spec, NotNull()) << "Invalid saved spec: " << error.data(); - mjModel* saved_model = mjs_compile(saved_spec, 0); + mjModel* saved_model = mj_compile(saved_spec, 0); EXPECT_THAT(saved_model, NotNull()) << "Invalid model: " << error.data(); mjtNum tol = 0; @@ -154,8 +154,8 @@ TEST_F(MujocoTest, SaveXml) { << "Expected and attached models are different!\n" << "Different field: " << field << '\n'; - mjs_deleteSpec(spec); - mjs_deleteSpec(saved_spec); + mj_deleteSpec(spec); + mj_deleteSpec(saved_spec); mj_deleteModel(model); mj_deleteModel(saved_model); } diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index c07c8e73..3a12e71c 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -24,10 +24,10 @@ #include #include #include +#include #include #include "src/cc/array_safety.h" #include "src/engine/engine_util_errmem.h" -#include "src/user/user_api.h" #include "src/xml/xml_api.h" #include "test/fixture.h" @@ -1101,7 +1101,7 @@ TEST_F(XMLReaderTest, ParseReplicateDefaultPropagate) { EXPECT_THAT(def, NotNull()); EXPECT_THAT(def->geom->type, mjGEOM_CAPSULE); - mjs_deleteSpec(spec); + mj_deleteSpec(spec); } // ----------------------- test camera parsing --------------------------------- diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index d81ebb90..d3c68f8e 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -57,6 +57,7 @@ public const bool mjEXTERNC = true; public const bool THIRD_PARTY_MUJOCO_MJRENDER_H_ = true; public const int mjNAUX = 10; public const int mjMAXTEXTURE = 1000; +public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJSPEC_H_ = true; public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJTHREAD_H_ = true; public const int mjMAXTHREAD = 128; public const bool THIRD_PARTY_MUJOCO_INCLUDE_MJTNUM_H_ = true; @@ -427,6 +428,39 @@ public enum mjtFont : int{ mjFONT_SHADOW = 1, mjFONT_BIG = 2, } +public enum mjtGeomInertia : int{ + mjINERTIA_VOLUME = 1, + mjINERTIA_SHELL = 2, +} +public enum mjtBuiltin : int{ + mjBUILTIN_NONE = 0, + mjBUILTIN_GRADIENT = 1, + mjBUILTIN_CHECKER = 2, + mjBUILTIN_FLAT = 3, +} +public enum mjtMark : int{ + mjMARK_NONE = 0, + mjMARK_EDGE = 1, + mjMARK_CROSS = 2, + mjMARK_RANDOM = 3, +} +public enum mjtLimited : int{ + mjLIMITED_FALSE = 0, + mjLIMITED_TRUE = 1, + mjLIMITED_AUTO = 2, +} +public enum mjtInertiaFromGeom : int{ + mjINERTIAFROMGEOM_FALSE = 0, + mjINERTIAFROMGEOM_TRUE = 1, + mjINERTIAFROMGEOM_AUTO = 2, +} +public enum mjtOrientation : int{ + mjORIENTATION_QUAT = 0, + mjORIENTATION_AXISANGLE = 1, + mjORIENTATION_XYAXES = 2, + mjORIENTATION_ZAXIS = 3, + mjORIENTATION_EULER = 4, +} public enum mjtTaskStatus : int{ mjTASK_NEW = 0, mjTASK_QUEUED = 1, @@ -6358,9 +6392,6 @@ public static unsafe extern int mj_saveLastXML([MarshalAs(UnmanagedType.LPStr)]s [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mj_freeLastXML(); -[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] -public static unsafe extern int mj_printSchema([MarshalAs(UnmanagedType.LPStr)]string filename, StringBuilder buffer, int buffer_sz, int flg_html, int flg_pad); - [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mj_step(mjModel_* m, mjData_* d); @@ -6469,6 +6500,9 @@ public static unsafe extern void mju_printMat(double* mat, int nr, int nc); [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mju_printMatSparse(double* mat, int nr, int* rownnz, int* rowadr, int* colind); +[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] +public static unsafe extern int mj_printSchema([MarshalAs(UnmanagedType.LPStr)]string filename, StringBuilder buffer, int buffer_sz, int flg_html, int flg_pad); + [DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)] public static unsafe extern void mj_fwdPosition(mjModel_* m, mjData_* d); From 238736393b226547d71146cf9c590a05a018e5b8 Mon Sep 17 00:00:00 2001 From: Google DeepMind Date: Sun, 9 Jun 2024 20:55:40 -0700 Subject: [PATCH 19/32] #mjx Make `Data.time` strongly typed. PiperOrigin-RevId: 641774798 Change-Id: I5e13e3cefeb7942083f623f29757a459656062dd --- mjx/mujoco/mjx/_src/io.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mjx/mujoco/mjx/_src/io.py b/mjx/mujoco/mjx/_src/io.py index 564020f6..efa1da86 100644 --- a/mjx/mujoco/mjx/_src/io.py +++ b/mjx/mujoco/mjx/_src/io.py @@ -174,7 +174,7 @@ def make_data(m: Union[types.Model, mujoco.MjModel]) -> types.Data: nefc=nefc, ncon=ncon, solver_niter=jp.array(0, dtype=int), - time=jp.array(0.0), + time=jp.array(0.0, dtype=float), qpos=jp.array(m.qpos0), qvel=zero_nv, act=zero_na, From 171b0d6e0646528ae4cf8299da8364dff1e7fd64 Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Mon, 10 Jun 2024 10:00:03 -0700 Subject: [PATCH 20/32] Improve order and categorization in function API reference. Also, move macros to the globals section. PiperOrigin-RevId: 641936967 Change-Id: I5b1f6aa52c69e68cc6310093c5811f0f20828bab --- doc/APIreference/APIfunctions.rst | 141 +--- doc/APIreference/APIglobals.rst | 76 ++ doc/APIreference/functions.rst | 1200 ++++++++++++++--------------- 3 files changed, 718 insertions(+), 699 deletions(-) diff --git a/doc/APIreference/APIfunctions.rst b/doc/APIreference/APIfunctions.rst index 81a01e6d..b63981d2 100644 --- a/doc/APIreference/APIfunctions.rst +++ b/doc/APIreference/APIfunctions.rst @@ -9,105 +9,48 @@ large number of functions. However the functions that most users are likely to n API function can be classified as: -- :ref:`Parse and compile` an :ref:`mjModel` from XML files and assets. -- :ref:`Main simulation` entry points, including :ref:`mj_step`. -- :ref:`Support` functions requiring :ref:`mjModel` and :ref:`mjData`. -- :ref:`Components` of the simulation pipeline, called from :ref:`mj_step`, :ref:`mj_forward` and :ref:`mj_inverse`. -- :ref:`Sub components` of the simulation pipeline. -- :ref:`Ray collisions`. -- :ref:`Printing` of various quantities. -- :ref:`Virtual file system`, used to load assets from memory. -- :ref:`Initialization` of data structures. -- :ref:`Abstract interaction`: mouse control of cameras and perturbations. -- :ref:`Abstract Visualization`. -- :ref:`OpenGL rendering`. -- :ref:`UI framework`. -- :ref:`Error and memory`. -- :ref:`Aliases for C standard math` functions. -- :ref:`Vector math`. -- :ref:`Quaternions`. -- :ref:`Poses transformations`. -- :ref:`Matrix decompositions and solvers`. -- :ref:`Miscellaneous` functions. -- :ref:`Derivatives`. -- :ref:`Plugin` related functions. -- :ref:`Macros`. -- :ref:`Thread` related functions. +- **Main entry points** + - :ref:`Parse and compile` an :ref:`mjModel` from XML files and assets. + - :ref:`Main simulation` entry points, including :ref:`mj_step`. -.. TODO(b/273075045): Better category-label namespacing. +- **Support functions** + - :ref:`Support` functions requiring :ref:`mjModel` and :ref:`mjData`. + - Pipeline :ref:`components`, called from :ref:`mj_step`, :ref:`mj_forward` and :ref:`mj_inverse`. + - :ref:`Sub components` of the simulation pipeline. + - :ref:`Ray casting`. + - :ref:`Printing` of various quantities. + - :ref:`Virtual file system`, used to load assets from memory. + - :ref:`Initialization` of data structures. + - :ref:`Error and memory`. + - :ref:`Miscellaneous` functions. + +- **Visualization, Rendering, UI** + - :ref:`Abstract interaction`: mouse control of cameras and perturbations. + - :ref:`Abstract Visualization`. + - :ref:`OpenGL rendering`. + - :ref:`UI framework`. + +- **Threads, Plugins, Derivatives** + - :ref:`Derivatives`. + - :ref:`Thread` |-| -related functions. + - :ref:`Plugin` |-| -related functions. + +- **Math** + - Aliases for C :ref:`standard math` functions. + - :ref:`Vector math`. + - :ref:`Quaternions`. + - :ref:`Pose transformations`. + - :ref:`Matrix decompositions and solvers`. + +- **Model editing** + - :ref:`Attachment`. + - :ref:`Tree elements`. + - :ref:`Non-tree elements`. + - :ref:`Assets`. + - :ref:`Find and get utilities`. + - :ref:`Attribute setters`. + - :ref:`Attribute getters`. + - :ref:`Spec utilities`. + - :ref:`Element initialization`. .. include:: functions.rst - -.. _Macros: - -Macros -^^^^^^ - -.. _mjDISABLED: - -mjDISABLED -~~~~~~~~~~ - -.. code-block:: C - - #define mjDISABLED(x) (m->opt.disableflags & (x)) - -Check if a given standard feature has been disabled via the physics options, assuming mjModel\* m is defined. x is of -type :ref:`mjtDisableBit`. - - -.. _mjENABLED: - -mjENABLED -~~~~~~~~~ - -.. code-block:: C - - #define mjENABLED(x) (m->opt.enableflags & (x)) - -Check if a given optional feature has been enabled via the physics options, assuming mjModel\* m is defined. x is of -type :ref:`mjtEnableBit`. - - -.. _mjMAX: - -mjMAX -~~~~~ - -.. code-block:: C - - #define mjMAX(a,b) (((a) > (b)) ? (a) : (b)) - -Return maximum value. To avoid repeated evaluation with mjtNum types, use the function :ref:`mju_max`. - - -.. _mjMIN: - -mjMIN -~~~~~ - -.. code-block:: C - - #define mjMIN(a,b) (((a) < (b)) ? (a) : (b)) - -Return minimum value. To avoid repeated evaluation with mjtNum types, use the function :ref:`mju_min`. - - -.. _mjPLUGIN_LIB_INIT: - -mjPLUGIN_LIB_INIT -~~~~~~~~~~~~~~~~~ - -.. code-block:: C - - #define mjPLUGIN_LIB_INIT \ - static void _mjplugin_dllmain(void); \ - mjEXTERNC int __stdcall mjDLLMAIN(void* hinst, unsigned long reason, void* reserved) { \ - if (reason == 1) { \ - _mjplugin_dllmain(); \ - } \ - return 1; \ - } \ - static void _mjplugin_dllmain(void) - -Register a plugin as a dynamic library. See :ref:`plugin registration` for more details. diff --git a/doc/APIreference/APIglobals.rst b/doc/APIreference/APIglobals.rst index 2f4e0989..f1ad2c5b 100644 --- a/doc/APIreference/APIglobals.rst +++ b/doc/APIreference/APIglobals.rst @@ -13,6 +13,7 @@ Global variable and constant definitions can be classified as: - The :ref:`collision table` containing narrow-phase collision functions. - :ref:`String constants`. - :ref:`Numeric constants`. +- :ref:`Macros`. - :ref:`X Macros`. .. _glError: @@ -528,6 +529,81 @@ shown in the table below. Their names are in the format ``mjKEY_XXX``. They corr number with the same meaning but for the compiled library. +.. _Macros: + +Macros +^^^^^^ + +.. _mjDISABLED: + +mjDISABLED +~~~~~~~~~~ + +.. code-block:: C + + #define mjDISABLED(x) (m->opt.disableflags & (x)) + +Check if a given standard feature has been disabled via the physics options, assuming mjModel\* m is defined. x is of +type :ref:`mjtDisableBit`. + + +.. _mjENABLED: + +mjENABLED +~~~~~~~~~ + +.. code-block:: C + + #define mjENABLED(x) (m->opt.enableflags & (x)) + +Check if a given optional feature has been enabled via the physics options, assuming mjModel\* m is defined. x is of +type :ref:`mjtEnableBit`. + + +.. _mjMAX: + +mjMAX +~~~~~ + +.. code-block:: C + + #define mjMAX(a,b) (((a) > (b)) ? (a) : (b)) + +Return maximum value. To avoid repeated evaluation with mjtNum types, use the function :ref:`mju_max`. + + +.. _mjMIN: + +mjMIN +~~~~~ + +.. code-block:: C + + #define mjMIN(a,b) (((a) < (b)) ? (a) : (b)) + +Return minimum value. To avoid repeated evaluation with mjtNum types, use the function :ref:`mju_min`. + + +.. _mjPLUGIN_LIB_INIT: + +mjPLUGIN_LIB_INIT +~~~~~~~~~~~~~~~~~ + +.. code-block:: C + + #define mjPLUGIN_LIB_INIT \ + static void _mjplugin_dllmain(void); \ + mjEXTERNC int __stdcall mjDLLMAIN(void* hinst, unsigned long reason, void* reserved) { \ + if (reason == 1) { \ + _mjplugin_dllmain(); \ + } \ + return 1; \ + } \ + static void _mjplugin_dllmain(void) + +Register a plugin as a dynamic library. See :ref:`plugin registration` for more details. + + .. _tyXMacro: X Macros diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 951795ee..3ecf8df8 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -1,5 +1,5 @@ .. - AUTOGENERATE: DO NOT EDIT + AUTOGENERATED: DO NOT EDIT MANUALLY .. _Parseandcompile: @@ -1498,6 +1498,376 @@ mj_deleteSpec Free memory allocation in mjSpec. +.. _Errorandmemory: + +Error and memory +^^^^^^^^^^^^^^^^ + +.. _mju_error: + +mju_error +~~~~~~~~~ + +.. mujoco-include:: mju_error + +Main error function; does not return to caller. + +.. _mju_error_i: + +mju_error_i +~~~~~~~~~~~ + +.. mujoco-include:: mju_error_i + +Deprecated: use mju_error. + +.. _mju_error_s: + +mju_error_s +~~~~~~~~~~~ + +.. mujoco-include:: mju_error_s + +Deprecated: use mju_error. + +.. _mju_warning: + +mju_warning +~~~~~~~~~~~ + +.. mujoco-include:: mju_warning + +Main warning function; returns to caller. + +.. _mju_warning_i: + +mju_warning_i +~~~~~~~~~~~~~ + +.. mujoco-include:: mju_warning_i + +Deprecated: use mju_warning. + +.. _mju_warning_s: + +mju_warning_s +~~~~~~~~~~~~~ + +.. mujoco-include:: mju_warning_s + +Deprecated: use mju_warning. + +.. _mju_clearHandlers: + +mju_clearHandlers +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_clearHandlers + +Clear user error and memory handlers. + +.. _mju_malloc: + +mju_malloc +~~~~~~~~~~ + +.. mujoco-include:: mju_malloc + +Allocate memory; byte-align on 64; pad size to multiple of 64. + +.. _mju_free: + +mju_free +~~~~~~~~ + +.. mujoco-include:: mju_free + +Free memory, using free() by default. + +.. _mj_warning: + +mj_warning +~~~~~~~~~~ + +.. mujoco-include:: mj_warning + +High-level warning function: count warnings in mjData, print only the first. + +.. _mju_writeLog: + +mju_writeLog +~~~~~~~~~~~~ + +.. mujoco-include:: mju_writeLog + +Write [datetime, type: message] to MUJOCO_LOG.TXT. + +.. _mjs_getError: + +mjs_getError +~~~~~~~~~~~~ + +.. mujoco-include:: mjs_getError + +Get compiler error message from spec. + +.. _mjs_isWarning: + +mjs_isWarning +~~~~~~~~~~~~~ + +.. mujoco-include:: mjs_isWarning + +Return 1 if compiler error is a warning. + +.. _Miscellaneous: + +Miscellaneous +^^^^^^^^^^^^^ + +.. _mju_muscleGain: + +mju_muscleGain +~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_muscleGain + +Muscle active force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax). + +.. _mju_muscleBias: + +mju_muscleBias +~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_muscleBias + +Muscle passive force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax). + +.. _mju_muscleDynamics: + +mju_muscleDynamics +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_muscleDynamics + +Muscle activation dynamics, prm = (tau_act, tau_deact, smoothing_width). + +.. _mju_encodePyramid: + +mju_encodePyramid +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_encodePyramid + +Convert contact force to pyramid representation. + +.. _mju_decodePyramid: + +mju_decodePyramid +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_decodePyramid + +Convert pyramid representation to contact force. + +.. _mju_springDamper: + +mju_springDamper +~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_springDamper + +Integrate spring-damper analytically, return pos(dt). + +.. _mju_min: + +mju_min +~~~~~~~ + +.. mujoco-include:: mju_min + +Return min(a,b) with single evaluation of a and b. + +.. _mju_max: + +mju_max +~~~~~~~ + +.. mujoco-include:: mju_max + +Return max(a,b) with single evaluation of a and b. + +.. _mju_clip: + +mju_clip +~~~~~~~~ + +.. mujoco-include:: mju_clip + +Clip x to the range [min, max]. + +.. _mju_sign: + +mju_sign +~~~~~~~~ + +.. mujoco-include:: mju_sign + +Return sign of x: +1, -1 or 0. + +.. _mju_round: + +mju_round +~~~~~~~~~ + +.. mujoco-include:: mju_round + +Round x to nearest integer. + +.. _mju_type2Str: + +mju_type2Str +~~~~~~~~~~~~ + +.. mujoco-include:: mju_type2Str + +Convert type id (mjtObj) to type name. + +.. _mju_str2Type: + +mju_str2Type +~~~~~~~~~~~~ + +.. mujoco-include:: mju_str2Type + +Convert type name to type id (mjtObj). + +.. _mju_writeNumBytes: + +mju_writeNumBytes +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_writeNumBytes + +Return human readable number of bytes using standard letter suffix. + +.. _mju_warningText: + +mju_warningText +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_warningText + +Construct a warning message given the warning type and info. + +.. _mju_isBad: + +mju_isBad +~~~~~~~~~ + +.. mujoco-include:: mju_isBad + +Return 1 if nan or abs(x)>mjMAXVAL, 0 otherwise. Used by check functions. + +.. _mju_isZero: + +mju_isZero +~~~~~~~~~~ + +.. mujoco-include:: mju_isZero + +Return 1 if all elements are 0. + +.. _mju_standardNormal: + +mju_standardNormal +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_standardNormal + +Standard normal random number generator (optional second number). + +.. _mju_f2n: + +mju_f2n +~~~~~~~ + +.. mujoco-include:: mju_f2n + +Convert from float to mjtNum. + +.. _mju_n2f: + +mju_n2f +~~~~~~~ + +.. mujoco-include:: mju_n2f + +Convert from mjtNum to float. + +.. _mju_d2n: + +mju_d2n +~~~~~~~ + +.. mujoco-include:: mju_d2n + +Convert from double to mjtNum. + +.. _mju_n2d: + +mju_n2d +~~~~~~~ + +.. mujoco-include:: mju_n2d + +Convert from mjtNum to double. + +.. _mju_insertionSort: + +mju_insertionSort +~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_insertionSort + +Insertion sort, resulting list is in increasing order. + +.. _mju_insertionSortInt: + +mju_insertionSortInt +~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_insertionSortInt + +Integer insertion sort, resulting list is in increasing order. + +.. _mju_Halton: + +mju_Halton +~~~~~~~~~~ + +.. mujoco-include:: mju_Halton + +Generate Halton sequence. + +.. _mju_strncpy: + +mju_strncpy +~~~~~~~~~~~ + +.. mujoco-include:: mju_strncpy + +Call strncpy, then set dst[n-1] = 0. + +.. _mju_sigmoid: + +mju_sigmoid +~~~~~~~~~~~ + +.. mujoco-include:: mju_sigmoid + +Sigmoid function over 0<=x<=1 using quintic polynomial. + .. _Interaction: Interaction @@ -2198,127 +2568,283 @@ This function is called in the screen refresh loop. It copies the offscreen Open there are multiple UIs in the application, it should be called once for each UI. Thus ``mjui_render`` is called all the time, while :ref:`mjui_update` is called only when changes in the UI take place. dsffsdg -.. _Errorandmemory: +.. _Derivatives-api: -Error and memory -^^^^^^^^^^^^^^^^ +Derivatives +^^^^^^^^^^^ -.. _mju_error: +The functions below provide useful derivatives of various functions, both analytic and +finite-differenced. The latter have names with the suffix ``FD``. Note that unlike much of the API, +outputs of derivative functions are the trailing rather than leading arguments. -mju_error -~~~~~~~~~ +.. _mjd_transitionFD: -.. mujoco-include:: mju_error +mjd_transitionFD +~~~~~~~~~~~~~~~~ -Main error function; does not return to caller. +.. mujoco-include:: mjd_transitionFD -.. _mju_error_i: +Finite-differenced discrete-time transition matrices. -mju_error_i -~~~~~~~~~~~ +Letting :math:`x, u` denote the current :ref:`state` and :ref:`control` +vector in an mjData instance, and letting :math:`y, s` denote the next state and sensor +values, the top-level :ref:`mj_step` function computes :math:`(x,u) \rightarrow (y,s)` +:ref:`mjd_transitionFD` computes the four associated Jacobians using finite-differencing. +These matrices and their dimensions are: -.. mujoco-include:: mju_error_i +.. csv-table:: + :header: "matrix", "Jacobian", "dimension" + :widths: auto + :align: left -Deprecated: use mju_error. + ``A``, :math:`\partial y / \partial x`, ``2*nv+na x 2*nv+na`` + ``B``, :math:`\partial y / \partial u`, ``2*nv+na x nu`` + ``C``, :math:`\partial s / \partial x`, ``nsensordata x 2*nv+na`` + ``D``, :math:`\partial s / \partial u`, ``nsensordata x nu`` -.. _mju_error_s: +- All outputs are optional (can be NULL). +- ``eps`` is the finite-differencing epsilon. +- ``flg_centered`` denotes whether to use forward (0) or centered (1) differences. +- Accuracy can be somewhat improved if solver :ref:`iterations` are set to a + fixed (small) value and solver :ref:`tolerance` is set to 0. This insures that + all calls to the solver will perform exactly the same number of iterations. -mju_error_s -~~~~~~~~~~~ +.. _mjd_inverseFD: -.. mujoco-include:: mju_error_s - -Deprecated: use mju_error. - -.. _mju_warning: - -mju_warning -~~~~~~~~~~~ - -.. mujoco-include:: mju_warning - -Main warning function; returns to caller. - -.. _mju_warning_i: - -mju_warning_i +mjd_inverseFD ~~~~~~~~~~~~~ -.. mujoco-include:: mju_warning_i +.. mujoco-include:: mjd_inverseFD -Deprecated: use mju_warning. +Finite differenced continuous-time inverse-dynamics Jacobians. -.. _mju_warning_s: +Letting :math:`x, a` denote the current :ref:`state` and acceleration vectors in an mjData instance, and +letting :math:`f, s` denote the forces computed by the inverse dynamics (``qfrc_inverse``), the function +:ref:`mj_inverse` computes :math:`(x,a) \rightarrow (f,s)`. :ref:`mjd_inverseFD` computes seven associated Jacobians +using finite-differencing. These matrices and their dimensions are: -mju_warning_s -~~~~~~~~~~~~~ +.. csv-table:: + :header: "matrix", "Jacobian", "dimension" + :widths: auto + :align: left -.. mujoco-include:: mju_warning_s + ``DfDq``, :math:`\partial f / \partial q`, ``nv x nv`` + ``DfDv``, :math:`\partial f / \partial v`, ``nv x nv`` + ``DfDa``, :math:`\partial f / \partial a`, ``nv x nv`` + ``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata`` + ``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata`` + ``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata`` + ``DmDq``, :math:`\partial M / \partial q`, ``nv x nM`` -Deprecated: use mju_warning. +- All outputs are optional (can be NULL). +- All outputs are transposed relative to Control Theory convention (i.e., column major). +- ``DmDq``, which contains a sparse representation of the ``nv x nv x nv`` tensor :math:`\partial M / \partial q`, is + not strictly an inverse dynamics Jacobian but is useful in related applications. It is provided as a convenience to + the user, since the required values are already computed if either of the other two :math:`\partial / \partial q` + Jacobians are requested. +- ``eps`` is the (forward) finite-differencing epsilon. +- ``flg_actuation`` denotes whether to subtract actuation forces (``qfrc_actuator``) from the output of the inverse + dynamics. If this flag is positive, actuator forces are not considered as external. -.. _mju_clearHandlers: +.. _mjd_subQuat: -mju_clearHandlers +mjd_subQuat +~~~~~~~~~~~ + +.. mujoco-include:: mjd_subQuat + +Derivatives of :ref:`mju_subQuat` (quaternion difference). + +.. _mjd_quatIntegrate: + +mjd_quatIntegrate ~~~~~~~~~~~~~~~~~ -.. mujoco-include:: mju_clearHandlers +.. mujoco-include:: mjd_quatIntegrate -Clear user error and memory handlers. +Derivatives of :ref:`mju_quatIntegrate`. -.. _mju_malloc: +:math:`{\tt \small mju\_quatIntegrate}(q, v, h)` performs the in-place rotation :math:`q \leftarrow q + v h`, +where :math:`q \in \mathbf{S}^3` is a unit quaternion, :math:`v \in \mathbf{R}^3` is a 3D angular velocity and +:math:`h \in \mathbf{R^+}` is a timestep. This is equivalent to :math:`{\tt \small mju\_quatIntegrate}(q, s, 1.0)`, +where :math:`s` is the scaled velocity :math:`s = h v`. -mju_malloc -~~~~~~~~~~ +:math:`{\tt \small mjd\_quatIntegrate}(v, h, D_q, D_v, D_h)` computes the Jacobians of the output :math:`q` with respect +to the inputs. Below, :math:`\bar q` denotes the pre-modified quaternion: -.. mujoco-include:: mju_malloc +.. math:: + \begin{aligned} + D_q &= \partial q / \partial \bar q \\ + D_v &= \partial q / \partial v \\ + D_h &= \partial q / \partial h + \end{aligned} -Allocate memory; byte-align on 64; pad size to multiple of 64. +Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math:`s = h v`). +All outputs are optional. -.. _mju_free: -mju_free -~~~~~~~~ +These functions provide high level manipulation for :ref:`mjSpec` structs, which represent an uncompiled :ref:`mjModel`. -.. mujoco-include:: mju_free +.. _Plugins-api: -Free memory, using free() by default. +Plugins +^^^^^^^ +.. _mjp_defaultPlugin: -.. _mj_warning: +mjp_defaultPlugin +~~~~~~~~~~~~~~~~~ -mj_warning -~~~~~~~~~~ +.. mujoco-include:: mjp_defaultPlugin -.. mujoco-include:: mj_warning +Set default plugin definition. -High-level warning function: count warnings in mjData, print only the first. +.. _mjp_registerPlugin: -.. _mju_writeLog: +mjp_registerPlugin +~~~~~~~~~~~~~~~~~~ -mju_writeLog -~~~~~~~~~~~~ +.. mujoco-include:: mjp_registerPlugin -.. mujoco-include:: mju_writeLog +Globally register a plugin. This function is thread-safe. +If an identical mjpPlugin is already registered, this function does nothing. +If a non-identical mjpPlugin with the same name is already registered, an mju_error is raised. +Two mjpPlugins are considered identical if all member function pointers and numbers are equal, +and the name and attribute strings are all identical, however the char pointers to the strings +need not be the same. -Write [datetime, type: message] to MUJOCO_LOG.TXT. +.. _mjp_pluginCount: -.. _mjs_getError: +mjp_pluginCount +~~~~~~~~~~~~~~~ -mjs_getError -~~~~~~~~~~~~ +.. mujoco-include:: mjp_pluginCount -.. mujoco-include:: mjs_getError +Return the number of globally registered plugins. -Get compiler error message from spec. +.. _mjp_getPlugin: -.. _mjs_isWarning: - -mjs_isWarning +mjp_getPlugin ~~~~~~~~~~~~~ -.. mujoco-include:: mjs_isWarning +.. mujoco-include:: mjp_getPlugin -Return 1 if compiler error is a warning. +Look up a plugin by name. If slot is not NULL, also write its registered slot number into it. + +.. _mjp_getPluginAtSlot: + +mjp_getPluginAtSlot +~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_getPluginAtSlot + +Look up a plugin by the registered slot number that was returned by mjp_registerPlugin. + +.. _mjp_defaultResourceProvider: + +mjp_defaultResourceProvider +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_defaultResourceProvider + +Set default resource provider definition. + +.. _mjp_registerResourceProvider: + +mjp_registerResourceProvider +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_registerResourceProvider + +Globally register a resource provider in a thread-safe manner. The provider must have a prefix +that is not a sub-prefix or super-prefix of any current registered providers. This function +returns a slot number > 0 on success. + +.. _mjp_resourceProviderCount: + +mjp_resourceProviderCount +~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_resourceProviderCount + +Return the number of globally registered resource providers. + +.. _mjp_getResourceProvider: + +mjp_getResourceProvider +~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_getResourceProvider + +Return the resource provider with the prefix that matches against the resource name. +If no match, return NULL. + +.. _mjp_getResourceProviderAtSlot: + +mjp_getResourceProviderAtSlot +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mjp_getResourceProviderAtSlot + +Look up a resource provider by slot number returned by mjp_registerResourceProvider. +If invalid slot number, return NULL. + +.. _Thread: + +Threads +^^^^^^^ +.. _mju_threadPoolCreate: + +mju_threadPoolCreate +~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_threadPoolCreate + +Create a thread pool with the specified number of threads running. + +.. _mju_bindThreadPool: + +mju_bindThreadPool +~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_bindThreadPool + +Adds a thread pool to mjData and configures it for multi-threaded use. + +.. _mju_threadPoolEnqueue: + +mju_threadPoolEnqueue +~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_threadPoolEnqueue + +Enqueue a task in a thread pool. + +.. _mju_threadPoolDestroy: + +mju_threadPoolDestroy +~~~~~~~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_threadPoolDestroy + +Destroy a thread pool. + +.. _mju_defaultTask: + +mju_defaultTask +~~~~~~~~~~~~~~~ + +.. mujoco-include:: mju_defaultTask + +Initialize an mjTask. + +.. _mju_taskJoin: + +mju_taskJoin +~~~~~~~~~~~~ + +.. mujoco-include:: mju_taskJoin + +Wait for a task to complete. .. _Standardmath: @@ -3199,532 +3725,6 @@ Allocate heap memory for box-constrained Quadratic Program. As in :ref:`mju_boxQP`, ``index``, ``lower``, and ``upper`` are optional. Free all pointers with ``mju_free()``. -.. _Miscellaneous: - -Miscellaneous -^^^^^^^^^^^^^ - -.. _mju_muscleGain: - -mju_muscleGain -~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_muscleGain - -Muscle active force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax). - -.. _mju_muscleBias: - -mju_muscleBias -~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_muscleBias - -Muscle passive force, prm = (range[2], force, scale, lmin, lmax, vmax, fpmax, fvmax). - -.. _mju_muscleDynamics: - -mju_muscleDynamics -~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_muscleDynamics - -Muscle activation dynamics, prm = (tau_act, tau_deact, smoothing_width). - -.. _mju_encodePyramid: - -mju_encodePyramid -~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_encodePyramid - -Convert contact force to pyramid representation. - -.. _mju_decodePyramid: - -mju_decodePyramid -~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_decodePyramid - -Convert pyramid representation to contact force. - -.. _mju_springDamper: - -mju_springDamper -~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_springDamper - -Integrate spring-damper analytically, return pos(dt). - -.. _mju_min: - -mju_min -~~~~~~~ - -.. mujoco-include:: mju_min - -Return min(a,b) with single evaluation of a and b. - -.. _mju_max: - -mju_max -~~~~~~~ - -.. mujoco-include:: mju_max - -Return max(a,b) with single evaluation of a and b. - -.. _mju_clip: - -mju_clip -~~~~~~~~ - -.. mujoco-include:: mju_clip - -Clip x to the range [min, max]. - -.. _mju_sign: - -mju_sign -~~~~~~~~ - -.. mujoco-include:: mju_sign - -Return sign of x: +1, -1 or 0. - -.. _mju_round: - -mju_round -~~~~~~~~~ - -.. mujoco-include:: mju_round - -Round x to nearest integer. - -.. _mju_type2Str: - -mju_type2Str -~~~~~~~~~~~~ - -.. mujoco-include:: mju_type2Str - -Convert type id (mjtObj) to type name. - -.. _mju_str2Type: - -mju_str2Type -~~~~~~~~~~~~ - -.. mujoco-include:: mju_str2Type - -Convert type name to type id (mjtObj). - -.. _mju_writeNumBytes: - -mju_writeNumBytes -~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_writeNumBytes - -Return human readable number of bytes using standard letter suffix. - -.. _mju_warningText: - -mju_warningText -~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_warningText - -Construct a warning message given the warning type and info. - -.. _mju_isBad: - -mju_isBad -~~~~~~~~~ - -.. mujoco-include:: mju_isBad - -Return 1 if nan or abs(x)>mjMAXVAL, 0 otherwise. Used by check functions. - -.. _mju_isZero: - -mju_isZero -~~~~~~~~~~ - -.. mujoco-include:: mju_isZero - -Return 1 if all elements are 0. - -.. _mju_standardNormal: - -mju_standardNormal -~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_standardNormal - -Standard normal random number generator (optional second number). - -.. _mju_f2n: - -mju_f2n -~~~~~~~ - -.. mujoco-include:: mju_f2n - -Convert from float to mjtNum. - -.. _mju_n2f: - -mju_n2f -~~~~~~~ - -.. mujoco-include:: mju_n2f - -Convert from mjtNum to float. - -.. _mju_d2n: - -mju_d2n -~~~~~~~ - -.. mujoco-include:: mju_d2n - -Convert from double to mjtNum. - -.. _mju_n2d: - -mju_n2d -~~~~~~~ - -.. mujoco-include:: mju_n2d - -Convert from mjtNum to double. - -.. _mju_insertionSort: - -mju_insertionSort -~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_insertionSort - -Insertion sort, resulting list is in increasing order. - -.. _mju_insertionSortInt: - -mju_insertionSortInt -~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_insertionSortInt - -Integer insertion sort, resulting list is in increasing order. - -.. _mju_Halton: - -mju_Halton -~~~~~~~~~~ - -.. mujoco-include:: mju_Halton - -Generate Halton sequence. - -.. _mju_strncpy: - -mju_strncpy -~~~~~~~~~~~ - -.. mujoco-include:: mju_strncpy - -Call strncpy, then set dst[n-1] = 0. - -.. _mju_sigmoid: - -mju_sigmoid -~~~~~~~~~~~ - -.. mujoco-include:: mju_sigmoid - -Sigmoid function over 0<=x<=1 using quintic polynomial. - -.. _Derivatives-api: - -Derivatives -^^^^^^^^^^^ - -The functions below provide useful derivatives of various functions, both analytic and -finite-differenced. The latter have names with the suffix ``FD``. Note that unlike much of the API, -outputs of derivative functions are the trailing rather than leading arguments. - -.. _mjd_transitionFD: - -mjd_transitionFD -~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjd_transitionFD - -Finite-differenced discrete-time transition matrices. - -Letting :math:`x, u` denote the current :ref:`state` and :ref:`control` -vector in an mjData instance, and letting :math:`y, s` denote the next state and sensor -values, the top-level :ref:`mj_step` function computes :math:`(x,u) \rightarrow (y,s)` -:ref:`mjd_transitionFD` computes the four associated Jacobians using finite-differencing. -These matrices and their dimensions are: - -.. csv-table:: - :header: "matrix", "Jacobian", "dimension" - :widths: auto - :align: left - - ``A``, :math:`\partial y / \partial x`, ``2*nv+na x 2*nv+na`` - ``B``, :math:`\partial y / \partial u`, ``2*nv+na x nu`` - ``C``, :math:`\partial s / \partial x`, ``nsensordata x 2*nv+na`` - ``D``, :math:`\partial s / \partial u`, ``nsensordata x nu`` - -- All outputs are optional (can be NULL). -- ``eps`` is the finite-differencing epsilon. -- ``flg_centered`` denotes whether to use forward (0) or centered (1) differences. -- Accuracy can be somewhat improved if solver :ref:`iterations` are set to a - fixed (small) value and solver :ref:`tolerance` is set to 0. This insures that - all calls to the solver will perform exactly the same number of iterations. - -.. _mjd_inverseFD: - -mjd_inverseFD -~~~~~~~~~~~~~ - -.. mujoco-include:: mjd_inverseFD - -Finite differenced continuous-time inverse-dynamics Jacobians. - -Letting :math:`x, a` denote the current :ref:`state` and acceleration vectors in an mjData instance, and -letting :math:`f, s` denote the forces computed by the inverse dynamics (``qfrc_inverse``), the function -:ref:`mj_inverse` computes :math:`(x,a) \rightarrow (f,s)`. :ref:`mjd_inverseFD` computes seven associated Jacobians -using finite-differencing. These matrices and their dimensions are: - -.. csv-table:: - :header: "matrix", "Jacobian", "dimension" - :widths: auto - :align: left - - ``DfDq``, :math:`\partial f / \partial q`, ``nv x nv`` - ``DfDv``, :math:`\partial f / \partial v`, ``nv x nv`` - ``DfDa``, :math:`\partial f / \partial a`, ``nv x nv`` - ``DsDq``, :math:`\partial s / \partial q`, ``nv x nsensordata`` - ``DsDv``, :math:`\partial s / \partial v`, ``nv x nsensordata`` - ``DsDa``, :math:`\partial s / \partial a`, ``nv x nsensordata`` - ``DmDq``, :math:`\partial M / \partial q`, ``nv x nM`` - -- All outputs are optional (can be NULL). -- All outputs are transposed relative to Control Theory convention (i.e., column major). -- ``DmDq``, which contains a sparse representation of the ``nv x nv x nv`` tensor :math:`\partial M / \partial q`, is - not strictly an inverse dynamics Jacobian but is useful in related applications. It is provided as a convenience to - the user, since the required values are already computed if either of the other two :math:`\partial / \partial q` - Jacobians are requested. -- ``eps`` is the (forward) finite-differencing epsilon. -- ``flg_actuation`` denotes whether to subtract actuation forces (``qfrc_actuator``) from the output of the inverse - dynamics. If this flag is positive, actuator forces are not considered as external. - -.. _mjd_subQuat: - -mjd_subQuat -~~~~~~~~~~~ - -.. mujoco-include:: mjd_subQuat - -Derivatives of :ref:`mju_subQuat` (quaternion difference). - -.. _mjd_quatIntegrate: - -mjd_quatIntegrate -~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjd_quatIntegrate - -Derivatives of :ref:`mju_quatIntegrate`. - -:math:`{\tt \small mju\_quatIntegrate}(q, v, h)` performs the in-place rotation :math:`q \leftarrow q + v h`, -where :math:`q \in \mathbf{S}^3` is a unit quaternion, :math:`v \in \mathbf{R}^3` is a 3D angular velocity and -:math:`h \in \mathbf{R^+}` is a timestep. This is equivalent to :math:`{\tt \small mju\_quatIntegrate}(q, s, 1.0)`, -where :math:`s` is the scaled velocity :math:`s = h v`. - -:math:`{\tt \small mjd\_quatIntegrate}(v, h, D_q, D_v, D_h)` computes the Jacobians of the output :math:`q` with respect -to the inputs. Below, :math:`\bar q` denotes the pre-modified quaternion: - -.. math:: - \begin{aligned} - D_q &= \partial q / \partial \bar q \\ - D_v &= \partial q / \partial v \\ - D_h &= \partial q / \partial h - \end{aligned} - -Note that derivatives depend only on :math:`h` and :math:`v` (in fact, on :math:`s = h v`). -All outputs are optional. - - -These functions provide high level manipulation for :ref:`mjSpec` structs, which represent an uncompiled :ref:`mjModel`. - -.. _Plugins-api: - -Plugins -^^^^^^^ -.. _mjp_defaultPlugin: - -mjp_defaultPlugin -~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_defaultPlugin - -Set default plugin definition. - -.. _mjp_registerPlugin: - -mjp_registerPlugin -~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_registerPlugin - -Globally register a plugin. This function is thread-safe. -If an identical mjpPlugin is already registered, this function does nothing. -If a non-identical mjpPlugin with the same name is already registered, an mju_error is raised. -Two mjpPlugins are considered identical if all member function pointers and numbers are equal, -and the name and attribute strings are all identical, however the char pointers to the strings -need not be the same. - -.. _mjp_pluginCount: - -mjp_pluginCount -~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_pluginCount - -Return the number of globally registered plugins. - -.. _mjp_getPlugin: - -mjp_getPlugin -~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_getPlugin - -Look up a plugin by name. If slot is not NULL, also write its registered slot number into it. - -.. _mjp_getPluginAtSlot: - -mjp_getPluginAtSlot -~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_getPluginAtSlot - -Look up a plugin by the registered slot number that was returned by mjp_registerPlugin. - -.. _mjp_defaultResourceProvider: - -mjp_defaultResourceProvider -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_defaultResourceProvider - -Set default resource provider definition. - -.. _mjp_registerResourceProvider: - -mjp_registerResourceProvider -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_registerResourceProvider - -Globally register a resource provider in a thread-safe manner. The provider must have a prefix -that is not a sub-prefix or super-prefix of any current registered providers. This function -returns a slot number > 0 on success. - -.. _mjp_resourceProviderCount: - -mjp_resourceProviderCount -~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_resourceProviderCount - -Return the number of globally registered resource providers. - -.. _mjp_getResourceProvider: - -mjp_getResourceProvider -~~~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_getResourceProvider - -Return the resource provider with the prefix that matches against the resource name. -If no match, return NULL. - -.. _mjp_getResourceProviderAtSlot: - -mjp_getResourceProviderAtSlot -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mjp_getResourceProviderAtSlot - -Look up a resource provider by slot number returned by mjp_registerResourceProvider. -If invalid slot number, return NULL. - -.. _Thread: - -Threads -^^^^^^^ -.. _mju_threadPoolCreate: - -mju_threadPoolCreate -~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_threadPoolCreate - -Create a thread pool with the specified number of threads running. - -.. _mju_bindThreadPool: - -mju_bindThreadPool -~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_bindThreadPool - -Adds a thread pool to mjData and configures it for multi-threaded use. - -.. _mju_threadPoolEnqueue: - -mju_threadPoolEnqueue -~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_threadPoolEnqueue - -Enqueue a task in a thread pool. - -.. _mju_threadPoolDestroy: - -mju_threadPoolDestroy -~~~~~~~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_threadPoolDestroy - -Destroy a thread pool. - -.. _mju_defaultTask: - -mju_defaultTask -~~~~~~~~~~~~~~~ - -.. mujoco-include:: mju_defaultTask - -Initialize an mjTask. - -.. _mju_taskJoin: - -mju_taskJoin -~~~~~~~~~~~~ - -.. mujoco-include:: mju_taskJoin - -Wait for a task to complete. - .. _Attachment: Attachment From 07fc95ca9a84ccbb616805ad6e0f7a6c777aeae5 Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Tue, 11 Jun 2024 09:21:39 -0700 Subject: [PATCH 21/32] Add orthographic cameras. Orthographic cameras are specified by setting the `orthographic` attribute of the `` element. The `fovy` attribute is still used to specify the field-of-view, but its semantic is different for orthographic cameras. For orthographic cameras, the field-of-view is expressed in units of length, rather than degrees. Other related changes: * Fix bug in the ordering of `cam_xxx` elements in `mjModel`. * Make camera visualization translucent only when the frustum is visualized. * Added a button to `simulate` to toggle between perspective and orthographic free cameras. https://youtu.be/ZXBTEIDWHhs PiperOrigin-RevId: 642293435 Change-Id: Id090a421ad88ab404b5b27ddbbd6bfc81ad49bc5 --- doc/XMLreference.rst | 30 ++- doc/XMLschema.rst | 26 +- doc/changelog.rst | 16 +- doc/includes/references.h | 23 +- include/mujoco/mjmodel.h | 12 +- include/mujoco/mjspec.h | 1 + include/mujoco/mjvisualize.h | 10 +- include/mujoco/mjxmacro.h | 7 +- introspect/structs.py | 81 ++++-- python/mujoco/structs.cc | 3 + simulate/simulate.cc | 5 +- src/engine/engine_io.c | 3 +- src/engine/engine_vis_init.c | 19 +- src/engine/engine_vis_interact.c | 68 +++-- src/engine/engine_vis_visualize.c | 255 ++++++++++-------- src/render/render_gl3.c | 20 +- src/user/user_model.cc | 1 + src/xml/xml_native_reader.cc | 34 ++- src/xml/xml_native_writer.cc | 23 +- .../testdata/vis_visualize/orthographic.xml | 34 +++ test/xml/xml_native_reader_test.cc | 30 +++ test/xml/xml_native_writer_test.cc | 10 + unity/Runtime/Bindings/MjBindings.cs | 14 +- 23 files changed, 490 insertions(+), 235 deletions(-) create mode 100644 test/engine/testdata/vis_visualize/orthographic.xml diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst index db20485c..61a9741f 100644 --- a/doc/XMLreference.rst +++ b/doc/XMLreference.rst @@ -2615,11 +2615,20 @@ and the +Y axis points up. Thus the frame position and orientation are the key a When the camera mode is "targetbody" or "targetbodycom", this attribute becomes required. It specifies which body should be targeted by the camera. In all other modes this attribute is ignored. +.. _body-camera-orthographic: + +:at:`orthographic`: :at-val:`[false, true], "false"` + Whether the camera uses a perspective projection (the default) or an orthographic projection. Setting this attribute + changes the semantic of the :ref:`fovy` attribute, see below. + .. _body-camera-fovy: :at:`fovy`: :at-val:`real, "45"` - Vertical field of view of the camera, expressed in degrees regardless of the global angle setting. The horizontal - field of view is computed automatically given the window size and the vertical field of view. + Vertical field-of-view of the camera. If the camera uses a perspective projection, the field-of-view is expressed in + degrees, regardless of the global :ref:`compiler/angle ` setting. If the camera uses an orthographic + projection, the field-of-view is expressed in units of length; note that in this case the default of 45 is too large + for most scenes and should likely be reduced. In either case, the horizontal field of view is computed automatically + given the window size and the vertical field of view. .. _body-camera-resolution: @@ -7157,14 +7166,22 @@ coordinated visual settings corresponding to a "theme", and then include this fi While all settings in mjVisual are global, the settings here could not be fit into any of the other subsections. So this is effectively a miscellaneous subsection. +.. _visual-global-orthographic: + +:at:`orthographic`: :at-val:`[false, true], "false"` + Whether the free camera uses a perspective projection (the default) or an orthographic projection. Setting this + attribute changes the semantic of the :ref:`global/fovy` attribute, see below. + .. _visual-global-fovy: :at:`fovy`: :at-val:`real, "45"` This attribute specifies the vertical field of view of the free camera, i.e., the camera that is always available in - the visualizer even if no cameras are explicitly defined in the model. It is always expressed in degrees, regardless - of the setting of the angle attribute of :ref:`compiler `, and is also represented in the low level model - in degrees. This is because we pass it to OpenGL which uses degrees. The same convention applies to the fovy - attribute of the :ref:`camera ` element below. + the visualizer even if no cameras are explicitly defined in the model. If the camera uses a perspective projection, + the field-of-view is expressed in degrees, regardless of the global :ref:`compiler/angle ` setting. + If the camera uses an orthographic projection, the field-of-view is expressed in units of length; note that in this + case the default of 45 is too large for most scenes and should likely be reduced. In either case, the horizontal + field of view is computed automatically given the window size and the vertical field of view. The same convention + applies to the :ref:`camera/fovy ` attribute. .. _visual-global-ipd: @@ -7869,6 +7886,7 @@ if omitted. | This element sets the attributes of the dummy :ref:`site ` element of the defaults class. | All site attributes are available here except: name, class. +.. _default-camera-orthographic: .. _default-camera-fovy: diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst index 044d022b..8f7d96c6 100644 --- a/doc/XMLschema.rst +++ b/doc/XMLschema.rst @@ -277,15 +277,15 @@ | :ref:`camera | \* | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`name` | :ref:`class` | :ref:`fovy` | :ref:`ipd` | | +| | | | :ref:`name` | :ref:`class` | :ref:`orthographic` | :ref:`fovy` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`resolution` | :ref:`pos` | :ref:`quat` | :ref:`axisangle` | | +| | | | :ref:`ipd` | :ref:`resolution` | :ref:`pos` | :ref:`quat` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`xyaxes` | :ref:`zaxis` | :ref:`euler` | :ref:`mode` | | +| | | | :ref:`axisangle` | :ref:`xyaxes` | :ref:`zaxis` | :ref:`euler` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`target` | :ref:`focal` | :ref:`focalpixel` | :ref:`principal` | | +| | | | :ref:`mode` | :ref:`target` | :ref:`focal` | :ref:`focalpixel` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`principalpixel` | :ref:`sensorsize` | :ref:`user` | | | +| | | | :ref:`principal` | :ref:`principalpixel` | :ref:`sensorsize` | :ref:`user` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| body |br| |_| |L| | | .. table:: | @@ -1246,11 +1246,11 @@ | :ref:`global | ? | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`fovy` | :ref:`ipd` | :ref:`azimuth` | :ref:`elevation` | | +| | | | :ref:`orthographic` | :ref:`fovy` | :ref:`ipd` | :ref:`azimuth` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`linewidth` | :ref:`glow` | :ref:`offwidth` | :ref:`offheight` | | +| | | | :ref:`elevation` | :ref:`linewidth` | :ref:`glow` | :ref:`offwidth` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`realtime` | :ref:`ellipsoidinertia` | :ref:`bvactive` | | | +| | | | :ref:`offheight` | :ref:`realtime` | :ref:`ellipsoidinertia` | :ref:`bvactive` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| visual |br| |_| |L| | | .. table:: | @@ -1396,13 +1396,15 @@ | :ref:`camera | ? | :class: mjcf-attributes | | ` | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`fovy` | :ref:`ipd` | :ref:`resolution` | :ref:`pos` | | +| | | | :ref:`orthographic` | :ref:`fovy` | :ref:`ipd` | :ref:`resolution` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`quat` | :ref:`axisangle` | :ref:`xyaxes` | :ref:`zaxis` | | +| | | | :ref:`pos` | :ref:`quat` | :ref:`axisangle` | :ref:`xyaxes` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`euler` | :ref:`mode` | :ref:`focal` | :ref:`focalpixel` | | +| | | | :ref:`zaxis` | :ref:`euler` | :ref:`mode` | :ref:`focal` | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | -| | | | :ref:`principal` | :ref:`principalpixel` | :ref:`sensorsize` | :ref:`user` | | +| | | | :ref:`focalpixel` | :ref:`principal` | :ref:`principalpixel` | :ref:`sensorsize` | | +| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +| | | | :ref:`user` | | | | | | | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ | +------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | |_| default |br| |_| |L| | | .. table:: | diff --git a/doc/changelog.rst b/doc/changelog.rst index c265aa27..f27194b8 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -13,15 +13,21 @@ General - Detailed documentation. - Python bindings. -2. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. +.. youtube:: ZXBTEIDWHhs + :align: right + :width: 240px + +2. Added support for orthographic cameras. This is available for both fixed cameras and the free camera, using the + :ref:`camera/orthographic` and :ref:`global/orthographic` + attributes, respectively. +3. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. MJX ~~~ - -3. Added support for :ref:`elliptic friction cones`. -4. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. -5. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. +4. Added support for :ref:`elliptic friction cones`. +5. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. +6. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. Version 3.1.6 (Jun 3, 2024) --------------------------- diff --git a/doc/includes/references.h b/doc/includes/references.h index 253ced07..105b03d2 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -751,7 +751,8 @@ struct mjOption_ { // physics options typedef struct mjOption_ mjOption; struct mjVisual_ { // visualization options struct { // global parameters - float fovy; // y-field of view for free camera (degrees) + int orthographic; // is the free camera orthographic (0: no, 1: yes) + float fovy; // y field-of-view of free camera (orthographic ? length : degree) float ipd; // inter-pupilary distance for free camera float azimuth; // initial azimuth of free camera (degrees) float elevation; // initial elevation of free camera (degrees) @@ -1072,11 +1073,12 @@ struct mjModel_ { mjtNum* cam_poscom0; // global position rel. to sub-com in qpos0 (ncam x 3) mjtNum* cam_pos0; // global position rel. to body in qpos0 (ncam x 3) mjtNum* cam_mat0; // global orientation in qpos0 (ncam x 9) - int* cam_resolution; // [width, height] in pixels (ncam x 2) - mjtNum* cam_fovy; // y-field of view (deg) (ncam x 1) - float* cam_intrinsic; // [focal length; principal point] (ncam x 4) - float* cam_sensorsize; // sensor size (ncam x 2) + int* cam_orthographic; // orthographic camera; 0: no, 1: yes (ncam x 1) + mjtNum* cam_fovy; // y field-of-view (ortho ? len : deg) (ncam x 1) mjtNum* cam_ipd; // inter-pupilary distance (ncam x 1) + int* cam_resolution; // resolution: pixels [width, height] (ncam x 2) + float* cam_sensorsize; // sensor size: length [width, height] (ncam x 2) + float* cam_intrinsic; // [focal length; principal point] (ncam x 4) mjtNum* cam_user; // user data (ncam x nuser_cam) // lights @@ -1858,6 +1860,7 @@ typedef struct mjsCamera_ { // camera specification mjString* targetbody; // target body for tracking/targeting // intrinsics + int orthographic; // is camera orthographic double fovy; // y-field of view double ipd; // inter-pupilary distance float intrinsic[4]; // camera intrinsics (length) @@ -2602,6 +2605,9 @@ struct mjvCamera_ { // abstract camera mjtNum distance; // distance to lookat point or tracked body mjtNum azimuth; // camera azimuth (deg) mjtNum elevation; // camera elevation (deg) + + // orthographic / perspective + int orthographic; // 0: perspective; 1: orthographic }; typedef struct mjvCamera_ mjvCamera; struct mjvGLCamera_ { // OpenGL camera @@ -2617,6 +2623,9 @@ struct mjvGLCamera_ { // OpenGL camera float frustum_top; // top float frustum_near; // near float frustum_far; // far + + // orthographic / perspective + int orthographic; // 0: perspective; 1: orthographic }; typedef struct mjvGLCamera_ mjvGLCamera; struct mjvGeom_ { // abstract geom @@ -2869,10 +2878,12 @@ struct mjvSceneState_ { mjtNum* site_size; float* site_rgba; + int* cam_orthographic; mjtNum* cam_fovy; mjtNum* cam_ipd; - float* cam_intrinsic; + int* cam_resolution; float* cam_sensorsize; + float* cam_intrinsic; mjtByte* light_directional; mjtByte* light_castshadow; diff --git a/include/mujoco/mjmodel.h b/include/mujoco/mjmodel.h index f8384c74..72999a31 100644 --- a/include/mujoco/mjmodel.h +++ b/include/mujoco/mjmodel.h @@ -458,7 +458,8 @@ typedef struct mjOption_ mjOption; struct mjVisual_ { // visualization options struct { // global parameters - float fovy; // y-field of view for free camera (degrees) + int orthographic; // is the free camera orthographic (0: no, 1: yes) + float fovy; // y field-of-view of free camera (orthographic ? length : degree) float ipd; // inter-pupilary distance for free camera float azimuth; // initial azimuth of free camera (degrees) float elevation; // initial elevation of free camera (degrees) @@ -787,11 +788,12 @@ struct mjModel_ { mjtNum* cam_poscom0; // global position rel. to sub-com in qpos0 (ncam x 3) mjtNum* cam_pos0; // global position rel. to body in qpos0 (ncam x 3) mjtNum* cam_mat0; // global orientation in qpos0 (ncam x 9) - int* cam_resolution; // [width, height] in pixels (ncam x 2) - mjtNum* cam_fovy; // y-field of view (deg) (ncam x 1) - float* cam_intrinsic; // [focal length; principal point] (ncam x 4) - float* cam_sensorsize; // sensor size (ncam x 2) + int* cam_orthographic; // orthographic camera; 0: no, 1: yes (ncam x 1) + mjtNum* cam_fovy; // y field-of-view (ortho ? len : deg) (ncam x 1) mjtNum* cam_ipd; // inter-pupilary distance (ncam x 1) + int* cam_resolution; // resolution: pixels [width, height] (ncam x 2) + float* cam_sensorsize; // sensor size: length [width, height] (ncam x 2) + float* cam_intrinsic; // [focal length; principal point] (ncam x 4) mjtNum* cam_user; // user data (ncam x nuser_cam) // lights diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h index a52f7d7d..91f284c7 100644 --- a/include/mujoco/mjspec.h +++ b/include/mujoco/mjspec.h @@ -345,6 +345,7 @@ typedef struct mjsCamera_ { // camera specification mjString* targetbody; // target body for tracking/targeting // intrinsics + int orthographic; // is camera orthographic double fovy; // y-field of view double ipd; // inter-pupilary distance float intrinsic[4]; // camera intrinsics (length) diff --git a/include/mujoco/mjvisualize.h b/include/mujoco/mjvisualize.h index 350f9c1e..0d23e39a 100644 --- a/include/mujoco/mjvisualize.h +++ b/include/mujoco/mjvisualize.h @@ -192,6 +192,9 @@ struct mjvCamera_ { // abstract camera mjtNum distance; // distance to lookat point or tracked body mjtNum azimuth; // camera azimuth (deg) mjtNum elevation; // camera elevation (deg) + + // orthographic / perspective + int orthographic; // 0: perspective; 1: orthographic }; typedef struct mjvCamera_ mjvCamera; @@ -211,6 +214,9 @@ struct mjvGLCamera_ { // OpenGL camera float frustum_top; // top float frustum_near; // near float frustum_far; // far + + // orthographic / perspective + int orthographic; // 0: perspective; 1: orthographic }; typedef struct mjvGLCamera_ mjvGLCamera; @@ -487,10 +493,12 @@ struct mjvSceneState_ { mjtNum* site_size; float* site_rgba; + int* cam_orthographic; mjtNum* cam_fovy; mjtNum* cam_ipd; - float* cam_intrinsic; + int* cam_resolution; float* cam_sensorsize; + float* cam_intrinsic; mjtByte* light_directional; mjtByte* light_castshadow; diff --git a/include/mujoco/mjxmacro.h b/include/mujoco/mjxmacro.h index 9cea2bc6..66a376eb 100644 --- a/include/mujoco/mjxmacro.h +++ b/include/mujoco/mjxmacro.h @@ -277,16 +277,17 @@ X ( int, cam_mode, ncam, 1 ) \ X ( int, cam_bodyid, ncam, 1 ) \ X ( int, cam_targetbodyid, ncam, 1 ) \ - X ( int, cam_resolution, ncam, 2 ) \ - XMJV( float, cam_sensorsize, ncam, 2 ) \ - XMJV( float, cam_intrinsic, ncam, 4 ) \ X ( mjtNum, cam_pos, ncam, 3 ) \ X ( mjtNum, cam_quat, ncam, 4 ) \ X ( mjtNum, cam_poscom0, ncam, 3 ) \ X ( mjtNum, cam_pos0, ncam, 3 ) \ X ( mjtNum, cam_mat0, ncam, 9 ) \ + XMJV( int, cam_orthographic, ncam, 1 ) \ XMJV( mjtNum, cam_fovy, ncam, 1 ) \ XMJV( mjtNum, cam_ipd, ncam, 1 ) \ + XMJV( int, cam_resolution, ncam, 2 ) \ + XMJV( float, cam_sensorsize, ncam, 2 ) \ + XMJV( float, cam_intrinsic, ncam, 4 ) \ X ( mjtNum, cam_user, ncam, MJ_M(nuser_cam) ) \ X ( int, light_mode, nlight, 1 ) \ X ( int, light_bodyid, nlight, 1 ) \ diff --git a/introspect/structs.py b/introspect/structs.py index cdd5ff1b..b3e92f77 100644 --- a/introspect/structs.py +++ b/introspect/structs.py @@ -310,10 +310,15 @@ STRUCTS: Mapping[str, StructDecl] = dict([ name='global', type=AnonymousStructDecl( fields=( + StructFieldDecl( + name='orthographic', + type=ValueType(name='int'), + doc='is the free camera orthographic (0: no, 1: yes)', # pylint: disable=line-too-long + ), StructFieldDecl( name='fovy', type=ValueType(name='float'), - doc='y-field of view for free camera (degrees)', + doc='y field-of-view of free camera (orthographic ? length : degree)', # pylint: disable=line-too-long ), StructFieldDecl( name='ipd', @@ -2024,32 +2029,18 @@ STRUCTS: Mapping[str, StructDecl] = dict([ doc='global orientation in qpos0 (ncam x 9)', ), StructFieldDecl( - name='cam_resolution', + name='cam_orthographic', type=PointerType( inner_type=ValueType(name='int'), ), - doc='[width, height] in pixels (ncam x 2)', + doc='orthographic camera; 0: no, 1: yes (ncam x 1)', ), StructFieldDecl( name='cam_fovy', type=PointerType( inner_type=ValueType(name='mjtNum'), ), - doc='y-field of view (deg) (ncam x 1)', - ), - StructFieldDecl( - name='cam_intrinsic', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='[focal length; principal point] (ncam x 4)', - ), - StructFieldDecl( - name='cam_sensorsize', - type=PointerType( - inner_type=ValueType(name='float'), - ), - doc='sensor size (ncam x 2)', + doc='y field-of-view (ortho ? len : deg) (ncam x 1)', ), StructFieldDecl( name='cam_ipd', @@ -2058,6 +2049,27 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='inter-pupilary distance (ncam x 1)', ), + StructFieldDecl( + name='cam_resolution', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='resolution: pixels [width, height] (ncam x 2)', + ), + StructFieldDecl( + name='cam_sensorsize', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='sensor size: length [width, height] (ncam x 2)', + ), + StructFieldDecl( + name='cam_intrinsic', + type=PointerType( + inner_type=ValueType(name='float'), + ), + doc='[focal length; principal point] (ncam x 4)', + ), StructFieldDecl( name='cam_user', type=PointerType( @@ -5367,6 +5379,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='mjtNum'), doc='camera elevation (deg)', ), + StructFieldDecl( + name='orthographic', + type=ValueType(name='int'), + doc='0: perspective; 1: orthographic', + ), ), )), ('mjvGLCamera', @@ -5428,6 +5445,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ type=ValueType(name='float'), doc='far', ), + StructFieldDecl( + name='orthographic', + type=ValueType(name='int'), + doc='0: perspective; 1: orthographic', + ), ), )), ('mjvGeom', @@ -6701,6 +6723,13 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='', ), + StructFieldDecl( + name='cam_orthographic', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), StructFieldDecl( name='cam_fovy', type=PointerType( @@ -6716,14 +6745,21 @@ STRUCTS: Mapping[str, StructDecl] = dict([ doc='', ), StructFieldDecl( - name='cam_intrinsic', + name='cam_resolution', + type=PointerType( + inner_type=ValueType(name='int'), + ), + doc='', + ), + StructFieldDecl( + name='cam_sensorsize', type=PointerType( inner_type=ValueType(name='float'), ), doc='', ), StructFieldDecl( - name='cam_sensorsize', + name='cam_intrinsic', type=PointerType( inner_type=ValueType(name='float'), ), @@ -9216,6 +9252,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([ ), doc='target body for tracking/targeting', ), + StructFieldDecl( + name='orthographic', + type=ValueType(name='int'), + doc='is camera orthographic', + ), StructFieldDecl( name='fovy', type=ValueType(name='double'), diff --git a/python/mujoco/structs.cc b/python/mujoco/structs.cc index ebe134a8..a52c8335 100644 --- a/python/mujoco/structs.cc +++ b/python/mujoco/structs.cc @@ -1390,6 +1390,7 @@ PYBIND11_MODULE(_structs, m) { }); DefineStructFunctions(mjVisualGlobal); #define X(var) mjVisualGlobal.def_readwrite(#var, &raw::MjVisualGlobal::var) + X(orthographic); X(fovy); X(ipd); X(azimuth); @@ -2123,6 +2124,7 @@ This is useful for example when the MJB is not available as a file on disk.)")); X(distance); X(azimuth); X(elevation); + X(orthographic); #undef X #define X(var) DefinePyArray(mjvCamera, #var, &MjvCameraWrapper::var) @@ -2153,6 +2155,7 @@ This is useful for example when the MJB is not available as a file on disk.)")); X(frustum_top); X(frustum_near); X(frustum_far); + X(orthographic); #undef X #define X(var) DefinePyArray(mjvGLCamera, #var, &MjvGLCameraWrapper::var) diff --git a/simulate/simulate.cc b/simulate/simulate.cc index f64eaf19..85f8d824 100644 --- a/simulate/simulate.cc +++ b/simulate/simulate.cc @@ -887,14 +887,15 @@ void MakeVisualizationSection(mj::Simulate* sim, const mjModel* m, int oldstate) {mjITEM_EDITFLOAT, "Ambient", 2, &(vis->headlight.ambient), "3"}, {mjITEM_EDITFLOAT, "Diffuse", 2, &(vis->headlight.diffuse), "3"}, {mjITEM_EDITFLOAT, "Specular", 2, &(vis->headlight.specular), "3"}, - {mjITEM_SEPARATOR, "Initial Free Camera", 1}, + {mjITEM_SEPARATOR, "Free Camera", 1}, + {mjITEM_RADIO, "Orthographic", 2, &(vis->global.orthographic), "No\nYes"}, + {mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"}, {mjITEM_EDITNUM, "Center", 2, &(stat->center), "3"}, {mjITEM_EDITFLOAT, "Azimuth", 2, &(vis->global.azimuth), "1"}, {mjITEM_EDITFLOAT, "Elevation", 2, &(vis->global.elevation), "1"}, {mjITEM_BUTTON, "Align", 2, nullptr, "CA"}, {mjITEM_SEPARATOR, "Global", 1}, {mjITEM_EDITNUM, "Extent", 2, &(stat->extent), "1"}, - {mjITEM_EDITFLOAT, "Field of view", 2, &(vis->global.fovy), "1"}, {mjITEM_RADIO, "Inertia", 5, &(vis->global.ellipsoidinertia), "Box\nEllipsoid"}, {mjITEM_RADIO, "BVH active", 5, &(vis->global.bvactive), "False\nTrue"}, {mjITEM_SEPARATOR, "Map", 1}, diff --git a/src/engine/engine_io.c b/src/engine/engine_io.c index c41932eb..5e5af9ae 100644 --- a/src/engine/engine_io.c +++ b/src/engine/engine_io.c @@ -182,6 +182,7 @@ static void setf4(float* rgba, float r, float g, float b, float a) { // set visual options to default values void mj_defaultVisual(mjVisual* vis) { // global + vis->global.orthographic = 0; vis->global.fovy = 45; vis->global.ipd = 0.068; vis->global.azimuth = 90; @@ -257,7 +258,7 @@ void mj_defaultVisual(mjVisual* vis) { setf4(vis->rgba.actuatornegative, .2, .6, .9, 1.); setf4(vis->rgba.actuatorpositive, .9, .4, .2, 1.); setf4(vis->rgba.com, .9, .9, .9, 1.); - setf4(vis->rgba.camera, .6, .9, .6, .3); + setf4(vis->rgba.camera, .6, .9, .6, 1); setf4(vis->rgba.light, .6, .6, .9, 1.); setf4(vis->rgba.selectpoint, .9, .9, .1, 1.); setf4(vis->rgba.connect, .2, .2, .8, 1.); diff --git a/src/engine/engine_vis_init.c b/src/engine/engine_vis_init.c index 3c16bf66..3e0d46a2 100644 --- a/src/engine/engine_vis_init.c +++ b/src/engine/engine_vis_init.c @@ -359,15 +359,16 @@ void mjv_defaultCamera(mjvCamera* cam) { void mjv_defaultFreeCamera(const mjModel* m, mjvCamera* cam) { memset(cam, 0, sizeof(mjvCamera)); - cam->type = mjCAMERA_FREE; - cam->fixedcamid = -1; - cam->trackbodyid = -1; - cam->lookat[0] = m->stat.center[0]; - cam->lookat[1] = m->stat.center[1]; - cam->lookat[2] = m->stat.center[2]; - cam->distance = 1.5 * m->stat.extent; - cam->azimuth = m->vis.global.azimuth; - cam->elevation = m->vis.global.elevation; + cam->type = mjCAMERA_FREE; + cam->fixedcamid = -1; + cam->trackbodyid = -1; + cam->lookat[0] = m->stat.center[0]; + cam->lookat[1] = m->stat.center[1]; + cam->lookat[2] = m->stat.center[2]; + cam->distance = 1.5 * m->stat.extent; + cam->azimuth = m->vis.global.azimuth; + cam->elevation = m->vis.global.elevation; + cam->orthographic = m->vis.global.orthographic; } diff --git a/src/engine/engine_vis_interact.c b/src/engine/engine_vis_interact.c index 62eec2af..604a97b6 100644 --- a/src/engine/engine_vis_interact.c +++ b/src/engine/engine_vis_interact.c @@ -223,16 +223,29 @@ void mjv_cameraInRoom(mjtNum* headpos, mjtNum* forward, mjtNum* up, const mjvSce // get frustum height at unit distance from camera; average left and right OpenGL cameras mjtNum mjv_frustumHeight(const mjvScene* scn) { - mjtNum height; + const mjvGLCamera* cam1 = scn->camera; + const mjvGLCamera* cam2 = scn->camera + 1; - // check znear - if (scn->camera[0].frustum_near < mjMINVAL || scn->camera[1].frustum_near < mjMINVAL) { - mjERROR("mjvScene frustum_near too small"); + if (cam1->orthographic != cam2->orthographic) { + mjERROR("cannot average frustums of perspective and orthographic cameras"); } - // add normalized height for left and right cameras - height = (scn->camera[0].frustum_top-scn->camera[0].frustum_bottom)/scn->camera[0].frustum_near + - (scn->camera[1].frustum_top-scn->camera[1].frustum_bottom)/scn->camera[1].frustum_near; + // get height + mjtNum height; + if (!cam1->orthographic) { + // check znear + if (cam1->frustum_near < mjMINVAL || cam2->frustum_near < mjMINVAL) { + mjERROR("mjvScene frustum_near too small"); + } + + // add normalized height for left and right cameras + height = (cam1->frustum_top - cam1->frustum_bottom) / cam1->frustum_near + + (cam2->frustum_top - cam2->frustum_bottom) / cam2->frustum_near; + } else { + // add height for left and right cameras + height = (cam1->frustum_top - cam1->frustum_bottom) + + (cam2->frustum_top - cam2->frustum_bottom); + } // average return 0.5*height; @@ -337,6 +350,9 @@ void mjv_moveCamera(const mjModel* m, int action, mjtNum reldx, mjtNum reldy, mju_sub3(dif, cam->lookat, headpos); scl = mjv_frustumHeight(scn) * mju_dot3(dif, forward); + // multiply by mystery coefficient TODO: b/346130949 + if (cam->orthographic) scl *= 0.15; + // move lookat point in opposite direction mju_addToScl3(cam->lookat, vec, -scl); break; @@ -563,6 +579,9 @@ void mjv_initPerturb(const mjModel* m, mjData* d, const mjvScene* scn, mjvPertur mju_sub3(dif, pert->refselpos, headpos); pert->scale = mjv_frustumHeight(scn) * mju_dot3(dif, forward); + // multiply by mystery coefficient TODO: b/346130949 + if (scn->camera[0].orthographic) pert->scale *= 0.15; + mj_freeStack(d); } @@ -733,6 +752,12 @@ mjvGLCamera mjv_averageCamera(const mjvGLCamera* cam1, const mjvGLCamera* cam2) cam.frustum_near = 0.5f * (cam1->frustum_near + cam2->frustum_near); cam.frustum_far = 0.5f * (cam1->frustum_far + cam2->frustum_far); + if (cam1->orthographic != cam2->orthographic) { + mjERROR("cannot average perspective and orthographic cameras"); + } else { + cam.orthographic = cam1->orthographic; + } + return cam; } @@ -755,17 +780,31 @@ int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt, // compute frustum halfwidth so as to match viewport aspect ratio mjtNum halfwidth = 0.5*aspectratio*(cam.frustum_top - cam.frustum_bottom); - // construct ray + // compute up and left offsets from normalized cursor + mjtNum d_up = cam.frustum_bottom + rely*(cam.frustum_top-cam.frustum_bottom); + mjtNum d_left = -(cam.frustum_center + (2*relx-1)*halfwidth); + + // define ray mjtNum ray[3]; - mju_scl3(ray, forward, cam.frustum_near); - mju_addToScl3(ray, up, cam.frustum_bottom + rely*(cam.frustum_top-cam.frustum_bottom)); - mju_addToScl3(ray, left, -(cam.frustum_center + (2*relx-1)*halfwidth)); - mju_normalize3(ray); + + // construct ray for orthographic camera: fixed direction, modify pos + if (cam.orthographic) { + mju_copy3(ray, forward); + mju_addToScl3(pos, up, d_up); + mju_addToScl3(pos, left, d_left); + } + + // construct ray for perspective camera: fixed pos, modify direction + else { + mju_scl3(ray, forward, cam.frustum_near); + mju_addToScl3(ray, up, d_up); + mju_addToScl3(ray, left, d_left); + mju_normalize3(ray); + } // find intersection with geoms *geomid = -1; - mjtNum geomdist = mj_ray(m, d, pos, ray, vopt->geomgroup, - vopt->flags[mjVIS_STATIC], -1, geomid); + mjtNum geomdist = mj_ray(m, d, pos, ray, vopt->geomgroup, vopt->flags[mjVIS_STATIC], -1, geomid); // find intersection with flexes int flexbodyid = -1; @@ -851,7 +890,6 @@ int mjv_select(const mjModel* m, const mjData* d, const mjvOption* vopt, } } - // geom if (best == 0) { *flexid = -1; diff --git a/src/engine/engine_vis_visualize.c b/src/engine/engine_vis_visualize.c index f35df109..a38125e9 100644 --- a/src/engine/engine_vis_visualize.c +++ b/src/engine/engine_vis_visualize.c @@ -511,11 +511,11 @@ static int bodycategory(const mjModel* m, int bodyid) { // computes the camera frustum static void getFrustum(float zver[2], float zhor[2], float znear, - const float K[4], const float sensorsize[2]) { - zhor[0] = znear / K[0] * (sensorsize[0]/2.f - K[2]); - zhor[1] = znear / K[0] * (sensorsize[0]/2.f + K[2]); - zver[0] = znear / K[1] * (sensorsize[1]/2.f - K[3]); - zver[1] = znear / K[1] * (sensorsize[1]/2.f + K[3]); + const float intrinsic[4], const float sensorsize[2]) { + zhor[0] = znear / intrinsic[0] * (sensorsize[0]/2.f - intrinsic[2]); + zhor[1] = znear / intrinsic[0] * (sensorsize[0]/2.f + intrinsic[2]); + zver[0] = znear / intrinsic[1] * (sensorsize[1]/2.f - intrinsic[3]); + zver[1] = znear / intrinsic[1] * (sensorsize[1]/2.f + intrinsic[3]); } @@ -1502,11 +1502,97 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } } - // cameras + // cameras and frustums objtype = mjOBJ_CAMERA; category = mjCAT_DECOR; if (vopt->flags[mjVIS_CAMERA] && (category & catmask)) { for (int i=0; i < m->ncam; i++) { + // copy camera rgba + float cam_rgba[4]; + f2f(cam_rgba, m->vis.rgba.camera, 4); + + // draw frustum if sensorsize is defined + if (m->cam_sensorsize[2*i+1] > 0) { + // when drawing frustum, make camera translucent + cam_rgba[3] = 0.3; + + // locals + const float* rgba = m->vis.rgba.frustum; + mjtNum vnear[4][3], vfar[4][3]; + mjtNum center[3]; + mjtNum znear = m->vis.map.znear * m->stat.extent; + mjtNum zfar = m->vis.scale.frustum * scl; + float zver[2], zhor[2]; + + // get frustum + getFrustum(zver, zhor, znear, m->cam_intrinsic + 4*i, m->cam_sensorsize + 2*i); + + // frustum frame to convert from planes to vertex representation + mjtNum *cam_xpos = d->cam_xpos+3*i; + mjtNum *cam_xmat = d->cam_xmat+9*i; + mjtNum x[] = {cam_xmat[0], cam_xmat[3], cam_xmat[6]}; + mjtNum y[] = {cam_xmat[1], cam_xmat[4], cam_xmat[7]}; + mjtNum z[] = {cam_xmat[2], cam_xmat[5], cam_xmat[8]}; + + // vertices of the near plane + mju_addScl3(center, cam_xpos, z, -znear); + mju_addScl3(vnear[0], center, x, -zhor[0]); + mju_addScl3(vnear[1], center, x, zhor[1]); + mju_addScl3(vnear[2], center, x, zhor[1]); + mju_addScl3(vnear[3], center, x, -zhor[0]); + mju_addToScl3(vnear[0], y, -zver[0]); + mju_addToScl3(vnear[1], y, -zver[0]); + mju_addToScl3(vnear[2], y, zver[1]); + mju_addToScl3(vnear[3], y, zver[1]); + + // vertices of the far plane + zhor[0] *= zfar / znear; + zhor[1] *= zfar / znear; + zver[0] *= zfar / znear; + zver[1] *= zfar / znear; + mju_addScl3(center, cam_xpos, z, -zfar); + mju_addScl3(vfar[0], center, x, -zhor[0]); + mju_addScl3(vfar[1], center, x, zhor[1]); + mju_addScl3(vfar[2], center, x, zhor[1]); + mju_addScl3(vfar[3], center, x, -zhor[0]); + mju_addToScl3(vfar[0], y, -zver[0]); + mju_addToScl3(vfar[1], y, -zver[0]); + mju_addToScl3(vfar[2], y, zver[1]); + mju_addToScl3(vfar[3], y, zver[1]); + + // triangulation and wireframe of the frustum + for (int e=0; e < 4; e++) { + START + mju_sub3(x, vfar[e], vnear[e]); + mju_sub3(y, vnear[(e+1)%4], vnear[e]); + mju_cross(z, x, y); + mjtNum tri1[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)}; + mjtNum xmat1[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]}; + mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri1, vnear[e], xmat1, rgba); + FINISH + START + mju_sub3(y, vnear[(e+1)%4], vfar[e]); + mju_sub3(x, vfar[(e+1)%4], vfar[e]); + mju_cross(z, x, y); + mjtNum tri2[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)}; + mjtNum xmat2[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]}; + mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri2, vfar[e], xmat2, rgba); + FINISH + START + mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vnear[(e+1)%4]); + f2f(thisgeom->rgba, rgba, 4); + FINISH + START + mjv_connector(thisgeom, mjGEOM_LINE, 3, vfar[e], vfar[(e+1)%4]); + f2f(thisgeom->rgba, rgba, 4); + FINISH + START + mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vfar[e]); + f2f(thisgeom->rgba, rgba, 4); + FINISH + } + } + START // construct geom: camera body @@ -1516,7 +1602,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, thisgeom->size[2] = scl * m->vis.scale.camera * 0.4; mju_n2f(thisgeom->pos, d->cam_xpos+3*i, 3); mju_n2f(thisgeom->mat, d->cam_xmat+9*i, 9); - f2f(thisgeom->rgba, m->vis.rgba.camera, 4); + f2f(thisgeom->rgba, cam_rgba, 4); // vopt->label if (vopt->label == mjLABEL_CAMERA) { @@ -1539,7 +1625,7 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, thisgeom->size[1] = scl * m->vis.scale.camera * 0.4; thisgeom->size[2] = scl * m->vis.scale.camera * 0.3; mju_n2f(thisgeom->mat, d->cam_xmat+9*i, 9); - f2f(thisgeom->rgba, m->vis.rgba.camera, 4); + f2f(thisgeom->rgba, cam_rgba, 4); for (int k=0; k < 3; k++) { thisgeom->rgba[k] *= 0.5; // make lens body darker } @@ -1582,88 +1668,6 @@ void mjv_addGeoms(const mjModel* m, mjData* d, const mjvOption* vopt, } } - // camera frustum - if (vopt->flags[mjVIS_CAMERA]) { - objtype = mjOBJ_CAMERA; - category = mjCAT_DECOR; - const float* rgba = m->vis.rgba.frustum; - mjtNum vnear[4][3], vfar[4][3]; - mjtNum center[3]; - mjtNum znear = m->vis.map.znear * m->stat.extent; - mjtNum zfar = m->vis.scale.frustum * scl; - float zver[2], zhor[2]; - for (int i=0; i < m->ncam; i++) { - if (m->cam_sensorsize[2*i+1] == 0) { - continue; - } - getFrustum(zver, zhor, znear, m->cam_intrinsic + 4*i, m->cam_sensorsize + 2*i); - - // frustum frame to convert from planes to vertex representation - mjtNum *cam_xpos = d->cam_xpos+3*i; - mjtNum *cam_xmat = d->cam_xmat+9*i; - mjtNum x[] = {cam_xmat[0], cam_xmat[3], cam_xmat[6]}; - mjtNum y[] = {cam_xmat[1], cam_xmat[4], cam_xmat[7]}; - mjtNum z[] = {cam_xmat[2], cam_xmat[5], cam_xmat[8]}; - - // vertices of the near plane - mju_addScl3(center, cam_xpos, z, -znear); - mju_addScl3(vnear[0], center, x, -zhor[0]); - mju_addScl3(vnear[1], center, x, zhor[1]); - mju_addScl3(vnear[2], center, x, zhor[1]); - mju_addScl3(vnear[3], center, x, -zhor[0]); - mju_addToScl3(vnear[0], y, -zver[0]); - mju_addToScl3(vnear[1], y, -zver[0]); - mju_addToScl3(vnear[2], y, zver[1]); - mju_addToScl3(vnear[3], y, zver[1]); - - // vertices of the far plane - zhor[0] *= zfar / znear; - zhor[1] *= zfar / znear; - zver[0] *= zfar / znear; - zver[1] *= zfar / znear; - mju_addScl3(center, cam_xpos, z, -zfar); - mju_addScl3(vfar[0], center, x, -zhor[0]); - mju_addScl3(vfar[1], center, x, zhor[1]); - mju_addScl3(vfar[2], center, x, zhor[1]); - mju_addScl3(vfar[3], center, x, -zhor[0]); - mju_addToScl3(vfar[0], y, -zver[0]); - mju_addToScl3(vfar[1], y, -zver[0]); - mju_addToScl3(vfar[2], y, zver[1]); - mju_addToScl3(vfar[3], y, zver[1]); - - // triangulation and wireframe of the frustum - for (int e=0; e < 4; e++) { - START - mju_sub3(x, vfar[e], vnear[e]); - mju_sub3(y, vnear[(e+1)%4], vnear[e]); - mju_cross(z, x, y); - mjtNum tri1[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)}; - mjtNum xmat1[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]}; - mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri1, vnear[e], xmat1, rgba); - FINISH - START - mju_sub3(y, vnear[(e+1)%4], vfar[e]); - mju_sub3(x, vfar[(e+1)%4], vfar[e]); - mju_cross(z, x, y); - mjtNum tri2[3] = {mju_normalize3(x), mju_normalize3(y), mju_normalize3(z)}; - mjtNum xmat2[9] = {x[0], y[0], z[0], x[1], y[1], z[1], x[2], y[2], z[2]}; - mjv_initGeom(thisgeom, mjGEOM_TRIANGLE, tri2, vfar[e], xmat2, rgba); - FINISH - START - mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vnear[(e+1)%4]); - f2f(thisgeom->rgba, rgba, 4); - FINISH - START - mjv_connector(thisgeom, mjGEOM_LINE, 3, vfar[e], vfar[(e+1)%4]); - f2f(thisgeom->rgba, rgba, 4); - FINISH - START - mjv_connector(thisgeom, mjGEOM_LINE, 3, vnear[e], vfar[e]); - f2f(thisgeom->rgba, rgba, 4); - FINISH - } - } - } // lights objtype = mjOBJ_LIGHT; @@ -2131,28 +2135,31 @@ void mjv_makeLights(const mjModel* m, const mjData* d, mjvScene* scn) { // update camera only void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScene* scn) { - mjtNum ca, sa, ce, se, move[3], *mat; - mjtNum headpos[3], forward[3], up[3], right[3], ipd; - // return if nothing to do if (!m || !cam || cam->type == mjCAMERA_USER) { return; } - // initialize frustum - float zver[2], zhor[2] = {0, 0}; - float znear = m->vis.map.znear * m->stat.extent; - float zfar = m->vis.map.zfar * m->stat.extent; + // define extrinsics + mjtNum move[3]; + mjtNum headpos[3], forward[3], up[3], right[3]; - // get headpos, forward[3], up, right, ipd, fovy + // define intrinsics + int cid, orthographic = 0; + mjtNum fovy, ipd; + float* intrinsic = NULL; + float* sensorsize = NULL; + + // get headpos, forward, up, right, ipd, fovy, orthographic, intrinsic switch (cam->type) { case mjCAMERA_FREE: case mjCAMERA_TRACKING: // get global ipd ipd = m->vis.global.ipd; - // compute image size from global fovy - zver[0] = zver[1] = (float)znear * mju_tan(m->vis.global.fovy * (float)(mjPI/360.0)); + // get orthographic, fovy + orthographic = m->vis.global.orthographic; + fovy = m->vis.global.fovy; // move lookat for tracking if (cam->type == mjCAMERA_TRACKING) { @@ -2168,10 +2175,10 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen } // compute frame - ca = mju_cos(cam->azimuth/180.0*mjPI); - sa = mju_sin(cam->azimuth/180.0*mjPI); - ce = mju_cos(cam->elevation/180.0*mjPI); - se = mju_sin(cam->elevation/180.0*mjPI); + mjtNum ca = mju_cos(cam->azimuth/180.0*mjPI); + mjtNum sa = mju_sin(cam->azimuth/180.0*mjPI); + mjtNum ce = mju_cos(cam->elevation/180.0*mjPI); + mjtNum se = mju_sin(cam->elevation/180.0*mjPI); forward[0] = ce*ca; forward[1] = ce*sa; forward[2] = se; @@ -2184,25 +2191,27 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen mju_addScl3(headpos, cam->lookat, forward, -cam->distance); break; - case mjCAMERA_FIXED: { - // get id and check - int cid = cam->fixedcamid; + case mjCAMERA_FIXED: + // get id, check range + cid = cam->fixedcamid; if (cid < 0 || cid >= m->ncam) { mjERROR("fixed camera id is outside valid range"); } - // get camera-specific ipd and fovy + // get camera-specific ipd, orthographic, fovy ipd = m->cam_ipd[cid]; - // get frustum from intrinsics or from fovy + orthographic = m->cam_orthographic[cid]; + fovy = m->cam_fovy[cid]; + + // if positive sensorsize, get sensorsize and intrinsic if (m->cam_sensorsize[2*cid+1]) { - getFrustum(zver, zhor, znear, m->cam_intrinsic + 4*cid, m->cam_sensorsize + 2*cid); - } else { - zver[0] = zver[1] = (float)znear * mju_tan(m->cam_fovy[cid] * (float)(mjPI/360.0)); + sensorsize = m->cam_sensorsize + 2*cid; + intrinsic = m->cam_intrinsic + 4*cid; } // get pointer to camera orientation matrix - mat = d->cam_xmat + 9*cid; + mjtNum* mat = d->cam_xmat + 9*cid; // get frame forward[0] = -mat[2]; @@ -2215,13 +2224,26 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen right[1] = mat[3]; right[2] = mat[6]; mju_copy3(headpos, d->cam_xpos + 3*cid); - } - break; + break; default: mjERROR("unknown camera type"); } + // convert intrinsics to frustum parameters + float znear = m->vis.map.znear * m->stat.extent; + float zfar = m->vis.map.zfar * m->stat.extent; + float zver[2], zhor[2] = {0, 0}; + if (orthographic){ + zver[0] = zver[1] = fovy / 2; + } else { + if (!intrinsic) { + zver[0] = zver[1] = znear * mju_tan(fovy * mjPI/360.0); + } else { + getFrustum(zver, zhor, znear, intrinsic, sensorsize); + } + } + // compute GL cameras for (int view=0; view < 2; view++) { // set frame @@ -2231,6 +2253,9 @@ void mjv_updateCamera(const mjModel* m, const mjData* d, mjvCamera* cam, mjvScen scn->camera[view].up[i] = (float)up[i]; } + // set orthographic + scn->camera[view].orthographic = orthographic; + // set symmetric frustum using intrinsic camera matrix scn->camera[view].frustum_top = zver[1]; scn->camera[view].frustum_bottom = -zver[0]; diff --git a/src/render/render_gl3.c b/src/render/render_gl3.c index 6535874a..4591c4bb 100644 --- a/src/render/render_gl3.c +++ b/src/render/render_gl3.c @@ -694,7 +694,7 @@ static void initLights(mjvScene* scn) { // set projection and modelview static void setView(int view, mjrRect viewport, const mjvScene* scn, const mjrContext* con, - float* camProject, float* camView) { + float camProject[16], float camView[16]) { mjvGLCamera cam; // copy specified camera for stereo, average for mono (view = -1) @@ -709,24 +709,34 @@ static void setView(int view, mjrRect viewport, const mjvScene* scn, const mjrCo : 0.5f * (float)viewport.width / (float)viewport.height * (cam.frustum_top - cam.frustum_bottom); - // set projection + // prepare projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (mjGLAD_GL_ARB_clip_control) { // reverse Z rendering mapping [znear, zfar] -> [1, 0] (ndc) glTranslatef(0.0f, 0.0f, 0.5f); glScalef(1.0f, 1.0f, -0.5f); - } - else { + } else { // reverse Z rendering mapping without shift [znear, zfar] -> [1, -1] (ndc) glScalef(1.0f, 1.0f, -1.0f); } - glFrustum(cam.frustum_center - halfwidth, + + // set projection, orthographic or perspective + if (cam.orthographic) { + glOrtho(cam.frustum_center - halfwidth, cam.frustum_center + halfwidth, cam.frustum_bottom, cam.frustum_top, cam.frustum_near, cam.frustum_far); + } else { + glFrustum(cam.frustum_center - halfwidth, + cam.frustum_center + halfwidth, + cam.frustum_bottom, + cam.frustum_top, + cam.frustum_near, + cam.frustum_far); + } // save projection matrix if requested if (camProject) { diff --git a/src/user/user_model.cc b/src/user/user_model.cc index 3a48ace2..a8a0d77c 100644 --- a/src/user/user_model.cc +++ b/src/user/user_model.cc @@ -2040,6 +2040,7 @@ void mjCModel::CopyTree(mjModel* m) { m->cam_targetbodyid[cid] = pc->targetbodyid; copyvec(m->cam_pos+3*cid, pc->pos, 3); copyvec(m->cam_quat+4*cid, pc->quat, 4); + m->cam_orthographic[cid] = pc->orthographic; m->cam_fovy[cid] = (mjtNum)pc->fovy; m->cam_ipd[cid] = (mjtNum)pc->ipd; copyvec(m->cam_resolution+2*cid, pc->resolution, 2); diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc index 59aa680e..0e05d98c 100644 --- a/src/xml/xml_native_reader.cc +++ b/src/xml/xml_native_reader.cc @@ -123,8 +123,8 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { {"visual", "*", "0"}, {"<"}, - {"global", "?", "11", "fovy", "ipd", "azimuth", "elevation", "linewidth", "glow", - "offwidth", "offheight", "realtime", "ellipsoidinertia", "bvactive"}, + {"global", "?", "12", "orthographic", "fovy", "ipd", "azimuth", "elevation", "linewidth", + "glow", "offwidth", "offheight", "realtime", "ellipsoidinertia", "bvactive"}, {"quality", "?", "5", "shadowsize", "offsamples", "numslices", "numstacks", "numquads"}, {"headlight", "?", "4", "ambient", "diffuse", "specular", "active"}, @@ -160,9 +160,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { "hfield", "mesh", "fitscale", "rgba", "fluidshape", "fluidcoef", "user"}, {"site", "?", "13", "type", "group", "pos", "quat", "material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"}, - {"camera", "?", "16", "fovy", "ipd", "resolution", "pos", "quat", "axisangle", "xyaxes", - "zaxis", "euler", "mode", "focal", "focalpixel", "principal", "principalpixel", - "sensorsize", "user"}, + {"camera", "?", "17", "orthographic", "fovy", "ipd", "resolution", "pos", "quat", + "axisangle", "xyaxes", "zaxis", "euler", "mode", "focal", "focalpixel", + "principal", "principalpixel", "sensorsize", "user"}, {"light", "?", "13", "pos", "dir", "bulbradius", "directional", "castshadow", "active", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "mode"}, {"pair", "?", "7", "condim", "friction", "solref", "solreffriction", "solimp", @@ -268,9 +268,9 @@ const char* MJCF[nMJCF][mjXATTRNUM] = { {">"}, {"site", "*", "15", "name", "class", "type", "group", "pos", "quat", "material", "size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"}, - {"camera", "*", "19", "name", "class", "fovy", "ipd", "resolution", "pos", "quat", - "axisangle", "xyaxes", "zaxis", "euler", "mode", "target", "focal", "focalpixel", - "principal", "principalpixel", "sensorsize", "user"}, + {"camera", "*", "20", "name", "class", "orthographic", "fovy", "ipd", "resolution", "pos", + "quat", "axisangle", "xyaxes", "zaxis", "euler", "mode", "target", + "focal", "focalpixel", "principal", "principalpixel", "sensorsize", "user"}, {"light", "*", "16", "name", "class", "directional", "castshadow", "active", "pos", "dir", "bulbradius", "attenuation", "cutoff", "exponent", "ambient", "diffuse", "specular", "mode", "target"}, @@ -1742,6 +1742,10 @@ void mjXReader::OneCamera(XMLElement* elem, mjsCamera* pcam) { ReadAlternative(elem, pcam->alt); ReadAttr(elem, "ipd", 1, &pcam->ipd, text); + if (MapValue(elem, "orthographic", &n, bool_map, 2)) { + pcam->orthographic = (n==1); + } + bool has_principal = ReadAttr(elem, "principalpixel", 2, pcam->principal_pixel, text) || ReadAttr(elem, "principal", 2, pcam->principal_length, text); bool has_focal = ReadAttr(elem, "focalpixel", 2, pcam->focal_pixel, text) || @@ -2948,6 +2952,7 @@ void mjXReader::Visual(XMLElement* section) { string text, name; XMLElement* elem; mjVisual* vis = &model->visual; + int n; // iterate over child elements elem = FirstChildElement(section); @@ -2957,6 +2962,9 @@ void mjXReader::Visual(XMLElement* section) { // global sub-element if (name=="global") { + if (MapValue(elem, "orthographic", &n, bool_map, 2)) { + vis->global.orthographic = (n==1); + } ReadAttr(elem, "fovy", 1, &vis->global.fovy, text); ReadAttr(elem, "ipd", 1, &vis->global.ipd, text); ReadAttr(elem, "azimuth", 1, &vis->global.azimuth, text); @@ -2970,13 +2978,11 @@ void mjXReader::Visual(XMLElement* section) { throw mjXError(elem, "realtime must be greater than 0"); } } - int ellipsoidinertia; - if (MapValue(elem, "ellipsoidinertia", &ellipsoidinertia, bool_map, 2)) { - vis->global.ellipsoidinertia = (ellipsoidinertia==1); + if (MapValue(elem, "ellipsoidinertia", &n, bool_map, 2)) { + vis->global.ellipsoidinertia = (n==1); } - int bvactive; - if (MapValue(elem, "bvactive", &bvactive, bool_map, 2)) { - vis->global.bvactive = (bvactive==1); + if (MapValue(elem, "bvactive", &n, bool_map, 2)) { + vis->global.bvactive = (n==1); } } diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc index a9cda59a..c0f07e10 100644 --- a/src/xml/xml_native_writer.cc +++ b/src/xml/xml_native_writer.cc @@ -474,9 +474,7 @@ void mjXWriter::OneCamera(XMLElement* elem, const mjCCamera* pcam, mjCDef* def) WriteAttr(elem, "ipd", 1, &pcam->ipd, &def->Camera().ipd); WriteAttrKey(elem, "mode", camlight_map, camlight_sz, pcam->mode, def->Camera().mode); WriteAttr(elem, "resolution", 2, pcam->resolution, def->Camera().resolution); - - // resolution if positive - WriteAttr(elem, "resolution", 2, pcam->resolution, def->Camera().resolution); + WriteAttrKey(elem, "orthographic", bool_map, 2, pcam->orthographic, def->Camera().orthographic); // camera intrinsics if specified if (pcam->sensor_size[0]>0 && pcam->sensor_size[1]>0) { @@ -1012,15 +1010,16 @@ void mjXWriter::Visual(XMLElement* root) { // global elem = InsertEnd(section, "global"); - WriteAttr(elem, "fovy", 1, &vis->global.fovy, &visdef.global.fovy); - WriteAttr(elem, "ipd", 1, &vis->global.ipd, &visdef.global.ipd); - WriteAttr(elem, "azimuth", 1, &vis->global.azimuth, &visdef.global.azimuth); - WriteAttr(elem, "elevation", 1, &vis->global.elevation, &visdef.global.elevation); - WriteAttr(elem, "linewidth", 1, &vis->global.linewidth, &visdef.global.linewidth); - WriteAttr(elem, "glow", 1, &vis->global.glow, &visdef.global.glow); - WriteAttr(elem, "realtime", 1, &vis->global.realtime, &visdef.global.realtime); - WriteAttrInt(elem, "offwidth", vis->global.offwidth, visdef.global.offwidth); - WriteAttrInt(elem, "offheight", vis->global.offheight, visdef.global.offheight); + WriteAttrKey(elem, "orthographic", bool_map, 2, vis->global.orthographic, visdef.global.orthographic); + WriteAttr(elem, "fovy", 1, &vis->global.fovy, &visdef.global.fovy); + WriteAttr(elem, "ipd", 1, &vis->global.ipd, &visdef.global.ipd); + WriteAttr(elem, "azimuth", 1, &vis->global.azimuth, &visdef.global.azimuth); + WriteAttr(elem, "elevation", 1, &vis->global.elevation, &visdef.global.elevation); + WriteAttr(elem, "linewidth", 1, &vis->global.linewidth, &visdef.global.linewidth); + WriteAttr(elem, "glow", 1, &vis->global.glow, &visdef.global.glow); + WriteAttr(elem, "realtime", 1, &vis->global.realtime, &visdef.global.realtime); + WriteAttrInt(elem, "offwidth", vis->global.offwidth, visdef.global.offwidth); + WriteAttrInt(elem, "offheight", vis->global.offheight, visdef.global.offheight); WriteAttrKey(elem, "ellipsoidinertia", bool_map, 2, vis->global.ellipsoidinertia, visdef.global.ellipsoidinertia); WriteAttrKey(elem, "bvactive", bool_map, 2, vis->global.bvactive, visdef.global.bvactive); if (!elem->FirstAttribute()) { diff --git a/test/engine/testdata/vis_visualize/orthographic.xml b/test/engine/testdata/vis_visualize/orthographic.xml new file mode 100644 index 00000000..bdff71a3 --- /dev/null +++ b/test/engine/testdata/vis_visualize/orthographic.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc index 3a12e71c..1d13f7d1 100644 --- a/test/xml/xml_native_reader_test.cc +++ b/test/xml/xml_native_reader_test.cc @@ -1249,6 +1249,36 @@ TEST_F(XMLReaderTest, InvalidSkinGroup) { EXPECT_THAT( error.data(), HasSubstr("skin group must be between 0 and 5\nElement 'skin', line 7")); +} + +TEST_F(XMLReaderTest, Orthographic) { + static constexpr char xml[] = R"( + + + + + + + + + + + + + + + + )"; + std::array error; + mjModel* model = LoadModelFromString(xml, error.data(), error.size()); + EXPECT_THAT(model, NotNull()) << error.data(); + + EXPECT_EQ(model->vis.global.orthographic, 1); + EXPECT_EQ(model->cam_orthographic[0], 1); + EXPECT_EQ(model->cam_orthographic[1], 1); + EXPECT_EQ(model->cam_fovy[0], 1); + EXPECT_EQ(model->cam_fovy[1], 2); + mj_deleteModel(model); } diff --git a/test/xml/xml_native_writer_test.cc b/test/xml/xml_native_writer_test.cc index 92131025..2c62ff81 100644 --- a/test/xml/xml_native_writer_test.cc +++ b/test/xml/xml_native_writer_test.cc @@ -1118,6 +1118,16 @@ TEST_F(XMLWriterTest, TrimsDefaults) { mj_deleteModel(model); } +TEST_F(XMLWriterTest, DoesntSaveGlobal) { + static constexpr char xml[] = ""; + std::array error; + mjModel* model = LoadModelFromString(xml, error.data(), error.size()); + ASSERT_THAT(model, NotNull()) << error.data(); + std::string saved_xml = SaveAndReadXml(model); + EXPECT_THAT(saved_xml, Not(HasSubstr("global"))); + mj_deleteModel(model); +} + TEST_F(XMLWriterTest, InheritrangeSavedAsRange) { static constexpr char xml[] = R"( diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs index d3c68f8e..53b1ec48 100644 --- a/unity/Runtime/Bindings/MjBindings.cs +++ b/unity/Runtime/Bindings/MjBindings.cs @@ -5013,6 +5013,7 @@ public unsafe struct mjOption_ { [StructLayout(LayoutKind.Sequential)] public unsafe struct global { + public int orthographic; public float fovy; public float ipd; public float azimuth; @@ -5321,11 +5322,12 @@ public unsafe struct mjModel_ { public double* cam_poscom0; public double* cam_pos0; public double* cam_mat0; - public int* cam_resolution; + public int* cam_orthographic; public double* cam_fovy; - public float* cam_intrinsic; - public float* cam_sensorsize; public double* cam_ipd; + public int* cam_resolution; + public float* cam_sensorsize; + public float* cam_intrinsic; public double* cam_user; public int* light_mode; public int* light_bodyid; @@ -5858,6 +5860,7 @@ public unsafe struct mjvCamera_ { public double distance; public double azimuth; public double elevation; + public int orthographic; } [StructLayout(LayoutKind.Sequential)] @@ -5871,6 +5874,7 @@ public unsafe struct mjvGLCamera_ { public float frustum_top; public float frustum_near; public float frustum_far; + public int orthographic; } [StructLayout(LayoutKind.Sequential)] @@ -6184,10 +6188,12 @@ public unsafe struct model { public int* site_group; public double* site_size; public float* site_rgba; + public int* cam_orthographic; public double* cam_fovy; public double* cam_ipd; - public float* cam_intrinsic; + public int* cam_resolution; public float* cam_sensorsize; + public float* cam_intrinsic; public byte* light_directional; public byte* light_castshadow; public float* light_bulbradius; From 5ed464b489cd74dc6bcc0f7a8d08ee56b64a3f67 Mon Sep 17 00:00:00 2001 From: Baruch Tabanpour Date: Tue, 11 Jun 2024 11:07:06 -0700 Subject: [PATCH 22/32] Support spherical joint in URDF. PiperOrigin-RevId: 642326650 Change-Id: Iec766800bc9ab7e022bd586fc5881df576d01331 --- doc/changelog.rst | 8 ++-- src/xml/xml_urdf.cc | 13 +++++- test/xml/xml_urdf_test.cc | 92 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/doc/changelog.rst b/doc/changelog.rst index f27194b8..72025f13 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -21,13 +21,13 @@ General :ref:`camera/orthographic` and :ref:`global/orthographic` attributes, respectively. 3. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. - +4. Add support for ``ball`` joints in the URDF parser. MJX ~~~ -4. Added support for :ref:`elliptic friction cones`. -5. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. -6. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. +5. Added support for :ref:`elliptic friction cones`. +6. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. +7. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. Version 3.1.6 (Jun 3, 2024) --------------------------- diff --git a/src/xml/xml_urdf.cc b/src/xml/xml_urdf.cc index cf3b841d..e7d27d88 100644 --- a/src/xml/xml_urdf.cc +++ b/src/xml/xml_urdf.cc @@ -32,14 +32,15 @@ using tinyxml2::XMLElement; // URDF joint type -static const int urJoint_sz = 6; +static const int urJoint_sz = 7; static const mjMap urJoint_map[urJoint_sz] = { {"revolute", 0}, {"continuous", 1}, {"prismatic", 2}, {"fixed", 3}, {"floating", 4}, - {"planar", 5} + {"planar", 5}, + {"spherical", 6} // Bullet physics supports ball joints (non-standard URDF) }; @@ -474,6 +475,14 @@ void mjXURDF::Joint(XMLElement* joint_elem) { pjoint2->type = mjJNT_HINGE; mjuu_setvec(pjoint2->pos, 0, 0, 0); mjuu_copyvec(pjoint2->axis, axis, 3); + break; + + case 6: // ball joint + pjoint = mjs_addJoint(pbody, 0); + mjs_setString(pjoint->name, jntname.c_str()); + pjoint->type = mjJNT_BALL; + mjuu_setvec(pjoint->pos, 0, 0, 0); + mjuu_copyvec(pjoint->axis, axis, 3); } // dynamics element diff --git a/test/xml/xml_urdf_test.cc b/test/xml/xml_urdf_test.cc index 3d798a54..50a45da0 100644 --- a/test/xml/xml_urdf_test.cc +++ b/test/xml/xml_urdf_test.cc @@ -14,9 +14,11 @@ // Tests for xml/xml_api.cc. +#include #include #include #include +#include #include #include @@ -160,5 +162,95 @@ TEST_F(MujocoTest, CanLoadUrdfWithNonUniqueNamesVisualBeforeCollision) { mj_deleteModel(model); } +TEST_F(MujocoTest, ReadsJointTypes) { + static constexpr char urdf[] = R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + )"; + std::array error; + mjModel* model = LoadModelFromString(urdf, error.data(), error.size()); + ASSERT_THAT(model, NotNull()) << error.data(); + + constexpr float eps = 1e-6; + + std::vector joint_names = {"floating", "revolute", "spherical", + "prismatic"}; + std::vector expected_joint_types = { + mjtJoint::mjJNT_FREE, mjtJoint::mjJNT_HINGE, mjtJoint::mjJNT_BALL, + mjtJoint::mjJNT_SLIDE}; + std::vector> expected_axis = { + {0.0, 0.0, 1.0}, {0.707107, 0.0, -0.707107}, {0.0, 0.0, 1.0}, + {0.0, 0.0, 1.0} + }; + for (int i = 0; i < joint_names.size(); ++i) { + int id = mj_name2id(model, mjtObj::mjOBJ_JOINT, joint_names[i].c_str()); + EXPECT_EQ(model->jnt_type[id], expected_joint_types[i]); + EXPECT_NEAR(model->jnt_axis[3 * id], expected_axis[i][0], eps); + EXPECT_NEAR(model->jnt_axis[3 * id + 1], expected_axis[i][1], eps); + EXPECT_NEAR(model->jnt_axis[3 * id + 2], expected_axis[i][2], eps); + } + + mj_deleteModel(model); +} + } // namespace } // namespace mujoco From a660051a4cc94aadead823f316eb4dbc1a2327af Mon Sep 17 00:00:00 2001 From: fanmin shi Date: Mon, 3 Jun 2024 17:53:29 +0200 Subject: [PATCH 23/32] Consistent spacing in python/tutorial.ipynb --- python/tutorial.ipynb | 44 ++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/python/tutorial.ipynb b/python/tutorial.ipynb index 3c6e4643..1e2528bc 100644 --- a/python/tutorial.ipynb +++ b/python/tutorial.ipynb @@ -479,8 +479,8 @@ "\"\"\"\n", "model = mujoco.MjModel.from_xml_string(xml)\n", "data = mujoco.MjData(model)\n", - "renderer = mujoco.Renderer(model)\n", "\n", + "renderer = mujoco.Renderer(model)\n", "mujoco.mj_forward(model, data)\n", "renderer.update_scene(data)\n", "\n", @@ -509,6 +509,7 @@ "# Run this cell multiple times for different colors\n", "model.geom('red_box').rgba[:3] = np.random.rand(3)\n", "renderer.update_scene(data)\n", + "\n", "media.show_image(renderer.render())" ] }, @@ -545,6 +546,7 @@ " renderer.update_scene(data)\n", " pixels = renderer.render()\n", " frames.append(pixels)\n", + "\n", "media.show_video(frames, fps=framerate)" ] }, @@ -590,6 +592,7 @@ "duration = 3.8 # (seconds)\n", "framerate = 60 # (Hz)\n", "\n", + "# Simulate and display video.\n", "frames = []\n", "mujoco.mj_resetData(model, data)\n", "while data.time < duration:\n", @@ -599,7 +602,6 @@ " pixels = renderer.render()\n", " frames.append(pixels)\n", "\n", - "# Simulate and display video.\n", "media.show_video(frames, fps=framerate)" ] }, @@ -646,6 +648,7 @@ "model.opt.gravity = (0, 0, 10)\n", "print('flipped gravity', model.opt.gravity)\n", "\n", + "# Simulate and display video.\n", "frames = []\n", "mujoco.mj_resetData(model, data)\n", "while data.time < duration:\n", @@ -756,10 +759,12 @@ "\n", "\"\"\"\n", "model = mujoco.MjModel.from_xml_string(tippe_top)\n", - "renderer = mujoco.Renderer(model)\n", "data = mujoco.MjData(model)\n", + "renderer = mujoco.Renderer(model)\n", + "\n", "mujoco.mj_forward(model, data)\n", "renderer.update_scene(data, camera=\"closeup\")\n", + "\n", "media.show_image(renderer.render())" ] }, @@ -933,10 +938,12 @@ "\n", "\"\"\"\n", "model = mujoco.MjModel.from_xml_string(chaotic_pendulum)\n", - "renderer = mujoco.Renderer(model, 480, 640)\n", "data = mujoco.MjData(model)\n", + "renderer = mujoco.Renderer(model, 480, 640)\n", + "\n", "mujoco.mj_forward(model, data)\n", "renderer.update_scene(data, camera=\"fixed\")\n", + "\n", "media.show_image(renderer.render())" ] }, @@ -965,12 +972,10 @@ "frames = []\n", "renderer = mujoco.Renderer(model, 240, 320)\n", "\n", - "\n", "# set initial state\n", "mujoco.mj_resetData(model, data)\n", "data.joint('root').qvel = 10\n", "\n", - "\n", "# simulate and record frames\n", "frame = 0\n", "sim_time = 0\n", @@ -1176,14 +1181,13 @@ " ax.plot(sim_time, energy, label='timestep = {:2.2g}ms'.format(1000*dt))\n", " ax.set_yscale('log')\n", "\n", - "\n", "# finalize plot\n", "ax.set_ybound(1, 1e3)\n", "ax.set_title('energy')\n", "ax.set_ylabel('Joule')\n", "ax.set_xlabel('second')\n", "ax.legend(frameon=True, loc='lower right');\n", - "plt.tight_layout()\n" + "plt.tight_layout()" ] }, { @@ -1228,10 +1232,12 @@ "\n", "\"\"\"\n", "model = mujoco.MjModel.from_xml_string(free_body_MJCF)\n", - "renderer = mujoco.Renderer(model, 400, 600)\n", "data = mujoco.MjData(model)\n", + "renderer = mujoco.Renderer(model, 400, 600)\n", + "\n", "mujoco.mj_forward(model, data)\n", "renderer.update_scene(data, \"fixed\")\n", + "\n", "media.show_image(renderer.render())" ] }, @@ -1275,7 +1281,7 @@ "mujoco.mj_resetData(model, data)\n", "data.qvel[3:6] = 5*np.random.randn(3)\n", "\n", - "# simulate and render\n", + "# Simulate and display video.\n", "for i in range(n_frames):\n", " while data.time < i/120.0: #1/4x real time\n", " mujoco.mj_step(model, data)\n", @@ -1283,7 +1289,6 @@ " frame = renderer.render()\n", " frames.append(frame)\n", "\n", - "# show video\n", "media.show_video(frames, fps=30)" ] }, @@ -1437,7 +1442,7 @@ "data = mujoco.MjData(model)\n", "renderer = mujoco.Renderer(model, height, width)\n", "\n", - "# simulate and render\n", + "# Simulate and display video.\n", "mujoco.mj_resetData(model, data)\n", "for i in range(n_frames):\n", " while data.time < i/30.0:\n", @@ -1445,6 +1450,7 @@ " renderer.update_scene(data, \"y\")\n", " frame = renderer.render()\n", " frames.append(frame)\n", + "\n", "media.show_video(frames, fps=30)" ] }, @@ -1513,10 +1519,12 @@ "\n", "\"\"\"\n", "model = mujoco.MjModel.from_xml_string(MJCF)\n", - "renderer = mujoco.Renderer(model, 480, 480)\n", "data = mujoco.MjData(model)\n", + "renderer = mujoco.Renderer(model, 480, 480)\n", + "\n", "mujoco.mj_forward(model, data)\n", "renderer.update_scene(data, \"fixed\")\n", + "\n", "media.show_image(renderer.render())\n" ] }, @@ -1551,7 +1559,7 @@ "mujoco.mj_resetData(model, data)\n", "data.ctrl = 20\n", "\n", - "# simulate and render\n", + "# Simulate and display video.\n", "for i in range(n_frames):\n", " while data.time < i/fps:\n", " mujoco.mj_step(model, data)\n", @@ -1783,10 +1791,10 @@ "outputs": [], "source": [ "#@title Project from world to camera coordinates {vertical-output: true}\n", + "\n", "# reset the scene\n", "renderer.update_scene(data)\n", "\n", - "\n", "# Get the world coordinates of the box corners\n", "box_pos = data.geom_xpos[model.geom('red_box').id]\n", "box_mat = data.geom_xmat[model.geom('red_box').id].reshape(3, 3)\n", @@ -1897,6 +1905,7 @@ " modify_scene(renderer.scene)\n", " pixels = renderer.render()\n", " frames.append(pixels)\n", + "\n", "media.show_video(frames, fps=framerate)" ] }, @@ -1923,6 +1932,7 @@ "outputs": [], "source": [ "#@title Load the \"dominos\" model\n", + "\n", "dominos_xml = \"\"\"\n", "\n", " \n", @@ -2022,6 +2032,7 @@ "outputs": [], "source": [ "#@title Render from fixed camera\n", + "\n", "duration = 2.5 # (seconds)\n", "framerate = 60 # (Hz)\n", "\n", @@ -2034,6 +2045,7 @@ " renderer.update_scene(data, camera='top')\n", " pixels = renderer.render()\n", " frames.append(pixels)\n", + "\n", "media.show_video(frames, fps=framerate)" ] }, @@ -2047,6 +2059,7 @@ "outputs": [], "source": [ "#@title Render from moving camera\n", + "\n", "duration = 3 # (seconds)\n", "\n", "# find time when box is thrown (speed > 2cm/s)\n", @@ -2109,6 +2122,7 @@ " renderer.update_scene(data, cam)\n", " pixels = renderer.render()\n", " frames.append(pixels)\n", + "\n", "media.show_video(frames, fps=framerate)" ] } From f37f8408803bb6ee876a8fe7403a3efbfeca8f57 Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Wed, 12 Jun 2024 05:34:40 -0700 Subject: [PATCH 24/32] Improvements to control noise in `simulate`: - Use `ctrl` as filter state rather than external preallocated buffer. - Scale and clip to control ranges. - Exponential decay to the middle of the range. - Scale is now [0 1] (scaled to range). - Rate is now [0 20]. PiperOrigin-RevId: 642586333 Change-Id: Id45a0420736a6edcf09be1db53d9320e7df2c668 --- simulate/main.cc | 41 +++++++++++++++++++---------------------- simulate/simulate.h | 4 ++-- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/simulate/main.cc b/simulate/main.cc index 5ff59e76..035c03ad 100644 --- a/simulate/main.cc +++ b/simulate/main.cc @@ -56,9 +56,6 @@ const int kErrorLength = 1024; // load error string length mjModel* m = nullptr; mjData* d = nullptr; -// control noise variables -mjtNum* ctrlnoise = nullptr; - using Seconds = std::chrono::duration; @@ -278,10 +275,6 @@ void PhysicsLoop(mj::Simulate& sim) { d = dnew; mj_forward(m, d); - // allocate ctrlnoise - free(ctrlnoise); - ctrlnoise = (mjtNum*) malloc(sizeof(mjtNum)*m->nu); - mju_zero(ctrlnoise, m->nu); } else { sim.LoadMessageClear(); } @@ -306,10 +299,6 @@ void PhysicsLoop(mj::Simulate& sim) { d = dnew; mj_forward(m, d); - // allocate ctrlnoise - free(ctrlnoise); - ctrlnoise = static_cast(malloc(sizeof(mjtNum)*m->nu)); - mju_zero(ctrlnoise, m->nu); } else { sim.LoadMessageClear(); } @@ -341,17 +330,30 @@ void PhysicsLoop(mj::Simulate& sim) { double elapsedSim = d->time - syncSim; // inject noise - if (sim.ctrl_noise_std) { + if (sim.ctrl_noise_std > 0) { // convert rate and scale to discrete time (Ornstein–Uhlenbeck) - mjtNum rate = mju_exp(-m->opt.timestep / mju_max(sim.ctrl_noise_rate, mjMINVAL)); + mjtNum rate = mju_exp(-m->opt.timestep / sim.ctrl_noise_rate); mjtNum scale = sim.ctrl_noise_std * mju_sqrt(1-rate*rate); for (int i=0; inu; i++) { - // update noise - ctrlnoise[i] = rate * ctrlnoise[i] + scale * mju_standardNormal(nullptr); + mjtNum bottom = 0, top = 0, midpoint = 0, halfrange = 1; + if (m->actuator_ctrllimited[i]) { + bottom = m->actuator_ctrlrange[2*i]; + top = m->actuator_ctrlrange[2*i+1]; + midpoint = 0.5 * (top + bottom); // target of exponential decay + halfrange = 0.5 * (top - bottom); // scales noise + } - // apply noise - d->ctrl[i] = ctrlnoise[i]; + // exponential convergence to midpoint at ctrl_noise_rate + d->ctrl[i] = rate * d->ctrl[i] + (1-rate) * midpoint; + + // add noise + d->ctrl[i] += scale * halfrange * mju_standardNormal(nullptr); + + // clip to range + if (m->actuator_ctrllimited[i]) { + d->ctrl[i] = mju_clip(d->ctrl[i], bottom, top); + } } } @@ -442,10 +444,6 @@ void PhysicsThread(mj::Simulate* sim, const char* filename) { mj_forward(m, d); - // allocate ctrlnoise - free(ctrlnoise); - ctrlnoise = static_cast(malloc(sizeof(mjtNum)*m->nu)); - mju_zero(ctrlnoise, m->nu); } else { sim->LoadMessageClear(); } @@ -454,7 +452,6 @@ void PhysicsThread(mj::Simulate* sim, const char* filename) { PhysicsLoop(*sim); // delete everything we allocated - free(ctrlnoise); mj_deleteData(d); mj_deleteModel(m); } diff --git a/simulate/simulate.h b/simulate/simulate.h index e6aa9b25..7d9e2657 100644 --- a/simulate/simulate.h +++ b/simulate/simulate.h @@ -293,8 +293,8 @@ class Simulate { {mjITEM_SLIDERINT, "Key", 3, &this->key, "0 0"}, {mjITEM_BUTTON, "Load key", 3}, {mjITEM_BUTTON, "Save key", 3}, - {mjITEM_SLIDERNUM, "Noise scale", 5, &this->ctrl_noise_std, "0 2"}, - {mjITEM_SLIDERNUM, "Noise rate", 5, &this->ctrl_noise_rate, "0 2"}, + {mjITEM_SLIDERNUM, "Noise scale", 5, &this->ctrl_noise_std, "0 1"}, + {mjITEM_SLIDERNUM, "Noise rate", 5, &this->ctrl_noise_rate, "0 20"}, {mjITEM_SEPARATOR, "History", 1}, {mjITEM_SLIDERINT, "", 5, &this->scrub_index, "0 0"}, {mjITEM_END} From c9bcf8371e1443470b3709df24f4fa29fdaa7fbf Mon Sep 17 00:00:00 2001 From: Yuval Tassa Date: Wed, 12 Jun 2024 09:21:54 -0700 Subject: [PATCH 25/32] Add `mj_setKeyframe` function to save current state in k-th model keyframe. Fixes #1719. Also improve documentation of `mju_sigmoid`, was previously only documented in changelog. PiperOrigin-RevId: 642637458 Change-Id: I811a0bf8f881c8b76c9a62d2faec8fdec4e7585b --- doc/APIreference/functions.rst | 19 ++++++++++- doc/APIreference/functions_override.rst | 12 +++++++ doc/changelog.rst | 9 ++--- doc/includes/references.h | 1 + include/mujoco/mujoco.h | 3 ++ introspect/functions.py | 24 +++++++++++++ python/mujoco/bindings_test.py | 32 ++++++++++++++++++ python/mujoco/functions.cc | 1 + simulate/simulate.cc | 9 +---- src/engine/engine_support.c | 22 ++++++++++++ src/engine/engine_support.h | 2 ++ test/engine/engine_support_test.cc | 45 +++++++++++++++++++++++++ unity/Runtime/Bindings/MjBindings.cs | 3 ++ 13 files changed, 169 insertions(+), 13 deletions(-) diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst index 3ecf8df8..d47ca1d7 100644 --- a/doc/APIreference/functions.rst +++ b/doc/APIreference/functions.rst @@ -235,6 +235,15 @@ mj_setState Copy concatenated state components specified by ``spec`` from ``state`` into ``d``. The bits of the integer ``spec`` correspond to element fields of :ref:`mjtState`. Fails with :ref:`mju_error` if ``spec`` is invalid. +.. _mj_setKeyframe: + +mj_setKeyframe +~~~~~~~~~~~~~~ + +.. mujoco-include:: mj_setKeyframe + +Copy current state to the k-th model keyframe. + .. _mj_addContact: mj_addContact @@ -1866,7 +1875,15 @@ mju_sigmoid .. mujoco-include:: mju_sigmoid -Sigmoid function over 0<=x<=1 using quintic polynomial. +Twice continuously differentiable sigmoid function using a quintic polynomial: + +.. math:: + s(x) = + \begin{cases} + 0, & & x \le 0 \\ + 6x^5 - 15x^4 + 10x^3, & 0 \lt & x \lt 1 \\ + 1, & 1 \le & x \qquad + \end{cases} .. _Interaction: diff --git a/doc/APIreference/functions_override.rst b/doc/APIreference/functions_override.rst index cb212686..cdc4593e 100644 --- a/doc/APIreference/functions_override.rst +++ b/doc/APIreference/functions_override.rst @@ -533,6 +533,18 @@ Symmetrize square matrix :math:`R = \frac{1}{2}(M + M^T)`. .. _Miscellaneous: +.. _mju_sigmoid: + +Twice continuously differentiable sigmoid function using a quintic polynomial: + +.. math:: + s(x) = + \begin{cases} + 0, & & x \le 0 \\ + 6x^5 - 15x^4 + 10x^3, & 0 \lt & x \lt 1 \\ + 1, & 1 \le & x \qquad + \end{cases} + .. _Derivatives-api: The functions below provide useful derivatives of various functions, both analytic and diff --git a/doc/changelog.rst b/doc/changelog.rst index 72025f13..f643f499 100644 --- a/doc/changelog.rst +++ b/doc/changelog.rst @@ -21,13 +21,14 @@ General :ref:`camera/orthographic` and :ref:`global/orthographic` attributes, respectively. 3. Added :ref:`maxhullvert`, the maximum number of vertices in a mesh's convex hull. -4. Add support for ``ball`` joints in the URDF parser. +4. Added :ref:`mj_setKeyframe` for saving the current state into a model keyframe. +5. Added support for ``ball`` joints in the URDF parser ("spherical" in URDF). MJX ~~~ -5. Added support for :ref:`elliptic friction cones`. -6. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. -7. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. +6. Added support for :ref:`elliptic friction cones`. +7. Fixed a bug that resulted in less-optimal linesearch solutions for some difficult constraint settings. +8. Fixed a bug in the Newton solver that sometimes resulted in less-optimal gradients. Version 3.1.6 (Jun 3, 2024) --------------------------- diff --git a/doc/includes/references.h b/doc/includes/references.h index 105b03d2..2f1fb622 100644 --- a/doc/includes/references.h +++ b/doc/includes/references.h @@ -3171,6 +3171,7 @@ void mj_constraintUpdate(const mjModel* m, mjData* d, const mjtNum* jar, int mj_stateSize(const mjModel* m, unsigned int spec); void mj_getState(const mjModel* m, const mjData* d, mjtNum* state, unsigned int spec); void mj_setState(const mjModel* m, mjData* d, const mjtNum* state, unsigned int spec); +void mj_setKeyframe(mjModel* m, const mjData* d, int k); int mj_addContact(const mjModel* m, mjData* d, const mjContact* con); int mj_isPyramidal(const mjModel* m); int mj_isSparse(const mjModel* m); diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h index 4166543c..5c90a152 100644 --- a/include/mujoco/mujoco.h +++ b/include/mujoco/mujoco.h @@ -418,6 +418,9 @@ MJAPI void mj_getState(const mjModel* m, const mjData* d, mjtNum* state, unsigne // Set state. MJAPI void mj_setState(const mjModel* m, mjData* d, const mjtNum* state, unsigned int spec); +// Copy current state to the k-th model keyframe. +MJAPI void mj_setKeyframe(mjModel* m, const mjData* d, int k); + // Add contact to d->contact list; return 0 if success; 1 if buffer full. MJAPI int mj_addContact(const mjModel* m, mjData* d, const mjContact* con); diff --git a/introspect/functions.py b/introspect/functions.py index a4cb5820..1e5875aa 100644 --- a/introspect/functions.py +++ b/introspect/functions.py @@ -2244,6 +2244,30 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([ ), doc='Set state.', )), + ('mj_setKeyframe', + FunctionDecl( + name='mj_setKeyframe', + return_type=ValueType(name='void'), + parameters=( + FunctionParameterDecl( + name='m', + type=PointerType( + inner_type=ValueType(name='mjModel'), + ), + ), + FunctionParameterDecl( + name='d', + type=PointerType( + inner_type=ValueType(name='mjData', is_const=True), + ), + ), + FunctionParameterDecl( + name='k', + type=ValueType(name='int'), + ), + ), + doc='Copy current state to the k-th model keyframe.', + )), ('mj_addContact', FunctionDecl( name='mj_addContact', diff --git a/python/mujoco/bindings_test.py b/python/mujoco/bindings_test.py index 8faa6db6..4ea6b3a9 100644 --- a/python/mujoco/bindings_test.py +++ b/python/mujoco/bindings_test.py @@ -27,6 +27,7 @@ import numpy as np TEST_XML = r""" +