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