From 6147c2fc6e331aef417f5b446e488e59c5520dc2 Mon Sep 17 00:00:00 2001
From: stonfute
Date: Wed, 5 Jun 2024 15:56:23 +0200
Subject: [PATCH 001/426] Fix MjHingeJoint.cs range with MakeLocaleInvariant
---
unity/Runtime/Components/Joints/MjHingeJoint.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/unity/Runtime/Components/Joints/MjHingeJoint.cs b/unity/Runtime/Components/Joints/MjHingeJoint.cs
index 8549c738..8d84d649 100644
--- a/unity/Runtime/Components/Joints/MjHingeJoint.cs
+++ b/unity/Runtime/Components/Joints/MjHingeJoint.cs
@@ -83,7 +83,7 @@ namespace Mujoco {
if (RangeLower > RangeUpper) {
throw new ArgumentException("Lower range value can't be bigger than Higher");
}
- mjcf.SetAttribute("range", $"{RangeLower} {RangeUpper}");
+ mjcf.SetAttribute("range", MjEngineTool.MakeLocaleInvariant($"{RangeLower} {RangeUpper}"));
mjcf.SetAttribute("ref", $"{Configuration}");
return mjcf;
From 9504a9718279bfd9c2c7292c0a3fb426378d5a54 Mon Sep 17 00:00:00 2001
From: Andrew
Date: Sun, 20 Oct 2024 23:03:21 -0400
Subject: [PATCH 002/426] add citation and fix math symbol text color
---
mjx/training_apg.ipynb | 28 +++++++++++++++++++++++++---
1 file changed, 25 insertions(+), 3 deletions(-)
diff --git a/mjx/training_apg.ipynb b/mjx/training_apg.ipynb
index 4a7192dc..fa9bc8a5 100644
--- a/mjx/training_apg.ipynb
+++ b/mjx/training_apg.ipynb
@@ -66,10 +66,10 @@
"$$\n",
"\n",
"$$\n",
- "\\frac{\\partial x_t}{\\partial \\theta} = \\textcolor{Navy}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial x_{t-1}}}\\frac{\\partial x_{t-1}}{\\partial \\theta} + \\textcolor{Navy}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial a_{t-1}}} \\frac{\\partial a_{t-1}}{\\partial \\theta}\n",
+ "\\frac{\\partial x_t}{\\partial \\theta} = \\color{blue}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial x_{t-1}}}\\frac{\\partial x_{t-1}}{\\partial \\theta} + \\color{blue}{\\frac{\\partial f(x_{t-1}, a_{t-1})}{\\partial a_{t-1}}} \\frac{\\partial a_{t-1}}{\\partial \\theta}\n",
"$$\n",
"\n",
- "The navy-colored terms in the above expression are enabled by MJX's differentiability and are the key difference between FoPG's and ZoPG's. An important consideration is what these jacobians look like near contact points. To see why certain gradients within the jacobian can be pathological, imagine a hard sphere falling toward a block of marble. How does its velocity change with respect to distance ($\\frac{\\partial \\dot{z}_t}{\\partial z_t}$), the instant before it touches the ground? This is the case of an **uninformative gradient**, due to [hard contact](https://arxiv.org/html/2404.02887v1). Fortunately, the default contact settings in Mujoco are sufficiently [soft](https://mujoco.readthedocs.io/en/stable/computation/index.html#soft-contact-model) for learning via FoPG's. With soft contacts, the ground applies an increasing force on the ball as it penetrates it, unlike rigid contacts, which instantly provide enough force for deflection.\n",
+ "The blue-colored terms in the above expression are enabled by MJX's differentiability and are the key difference between FoPG's and ZoPG's. An important consideration is what these jacobians look like near contact points. To see why certain gradients within the jacobian can be pathological, imagine a hard sphere falling toward a block of marble. How does its velocity change with respect to distance ($\\frac{\\partial \\dot{z}_t}{\\partial z_t}$), the instant before it touches the ground? This is the case of an **uninformative gradient**, due to [hard contact](https://arxiv.org/html/2404.02887v1). Fortunately, the default contact settings in Mujoco are sufficiently [soft](https://mujoco.readthedocs.io/en/stable/computation/index.html#soft-contact-model) for learning via FoPG's. With soft contacts, the ground applies an increasing force on the ball as it penetrates it, unlike rigid contacts, which instantly provide enough force for deflection.\n",
"\n",
"A helpful way to think about FoPG's is via the chain rule and computation graphs, as illustrated below for how $r_2$ influences the policy gradient, again for the case that the reward does not depend on action:\n",
"\n",
@@ -85,7 +85,29 @@
"\n",
"Last, despite the sample efficiency, FoPG methods can still struggle with wall-clock time. Because the gradients have low variance, they do not benefit significantly from massive parallelization of data collection - unlike [RL](https://arxiv.org/abs/2109.11978). Additionally, the policy gradient is typically calculated via autodifferentiation. This can be 3-5x slower than unrolling the simulation forward, and memory intensive, with memory requirements scaling with $O(m \\cdot (m+n) \\cdot T)$, where m and n are the state and control dimensions, $m \\cdot (m+n)$ is the jacobian dimension, and T is the number of steps propogated through.\n",
"\n",
- "Note that with certain models, using autodifferentiation through mjx.step currently causes [nan gradients](https://github.com/google-deepmind/mujoco/issues/1517). For now, we address this issue by using double-precision floats, at the cost of doubling the memory requirements and training time."
+ "Note that with certain models, using autodifferentiation through mjx.step currently causes [nan gradients](https://github.com/google-deepmind/mujoco/issues/1517). For now, we address this issue by using double-precision floats, at the cost of doubling the memory requirements and training time.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "---\n",
+ "**Publications**\n",
+ "\n",
+ "If you use this work in an academic context, please cite the following publication:\n",
+ "\n",
+ "```\n",
+ "@misc{luo2024residual,\n",
+ " title={Residual Policy Learning for Perceptive Quadruped Control Using Differentiable Simulation},\n",
+ " author={Luo, Jing Yuan and Song, Yunlong and Klemm, Victor and Shi, Fan and Scaramuzza, Davide and Hutter, Marco},\n",
+ " year={2024},\n",
+ " eprint={2410.03076},\n",
+ " archivePrefix={arXiv},\n",
+ " primaryClass={cs.RO},\n",
+ " url={https://doi.org/10.48550/arXiv.2410.03076}\n",
+ "}\n",
+ "```"
]
},
{
From 7620aef530d41eb5553f364a84793212004e78a3 Mon Sep 17 00:00:00 2001
From: Yuval Tassa
Date: Wed, 23 Oct 2024 12:16:39 -0700
Subject: [PATCH 003/426] Fix typo in the changelog.
PiperOrigin-RevId: 689058361
Change-Id: I479efa8fc0b8d31030d3f6444b874a14419f3518
---
doc/changelog.rst | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/changelog.rst b/doc/changelog.rst
index d3ec0db6..4cb748dd 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -17,7 +17,7 @@ MJX
Bug fixes
^^^^^^^^^
-- Fixed a bug in slider-crank ref:`transmission`. The bug was introduced in 3.0.0.
+- Fixed a bug in slider-crank :ref:`transmission`. The bug was introduced in 3.0.0.
Version 3.2.4 (Oct 15, 2024)
----------------------------
From ca348632a9b04ae12d488be811e916f7d9a7cacc Mon Sep 17 00:00:00 2001
From: Kyle Bayes
Date: Thu, 24 Oct 2024 02:40:56 -0700
Subject: [PATCH 004/426] Change tolerances for ellipsoid-capsule and
ellipsoid-sphere collision tests in MJX.
PiperOrigin-RevId: 689301610
Change-Id: I86be65524d13a19abd0b378b0933a5fbbe0ecab8
---
mjx/mujoco/mjx/_src/collision_driver_test.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/mjx/mujoco/mjx/_src/collision_driver_test.py b/mjx/mujoco/mjx/_src/collision_driver_test.py
index 611703f1..d5c893d0 100644
--- a/mjx/mujoco/mjx/_src/collision_driver_test.py
+++ b/mjx/mujoco/mjx/_src/collision_driver_test.py
@@ -263,7 +263,7 @@ class EllipsoidCollisionTest(parameterized.TestCase):
self.assertLess(dx.contact.dist[0], 0)
for field in dataclasses.fields(Contact):
_assert_attr_eq(
- dx.contact, d.contact, field.name, 'ellipsoid-sphere', 1e-4)
+ dx.contact, d.contact, field.name, 'ellipsoid-sphere', 1e-3)
_ELLIPSOID_CAPSULE = """
@@ -285,7 +285,7 @@ class EllipsoidCollisionTest(parameterized.TestCase):
self.assertLess(dx.contact.dist[0], 0)
for field in dataclasses.fields(Contact):
_assert_attr_eq(
- dx.contact, d.contact, field.name, 'ellipsoid-capsule', 1e-4)
+ dx.contact, d.contact, field.name, 'ellipsoid-capsule', 1e-3)
_ELLIPSOID_CYLINDER = """
From 1043633cc366a4a563ab74b0bf6c07a306a47ca7 Mon Sep 17 00:00:00 2001
From: Kyle Bayes
Date: Thu, 24 Oct 2024 05:36:15 -0700
Subject: [PATCH 005/426] Remove arbitrary limit of a 1000 elements from
certain attributes. Fixes #2166.
PiperOrigin-RevId: 689343609
Change-Id: I7cbf5007884433807c76c6c309cb4a858479d34d
---
src/xml/xml_native_reader.cc | 38 +++++++++++++++++-------------------
1 file changed, 18 insertions(+), 20 deletions(-)
diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc
index 17fe2609..f145afbd 100644
--- a/src/xml/xml_native_reader.cc
+++ b/src/xml/xml_native_reader.cc
@@ -4258,8 +4258,6 @@ void mjXReader::Sensor(XMLElement* section) {
// keyframe section parser
void mjXReader::Keyframe(XMLElement* section) {
XMLElement* elem;
- int n;
- double data[1000];
// iterate over child elements
elem = FirstChildElement(section);
@@ -4275,39 +4273,39 @@ void mjXReader::Keyframe(XMLElement* section) {
ReadAttr(elem, "time", 1, &key->time, text);
// read qpos
- n = ReadAttr(elem, "qpos", 1000, data, text, false, false);
- if (n) {
- mjs_setDouble(key->qpos, data, n);
+ auto maybe_data = ReadAttrVec(elem, "qpos", false);
+ if (maybe_data.has_value()) {
+ mjs_setDouble(key->qpos, maybe_data->data(), maybe_data->size());
}
// read qvel
- n = ReadAttr(elem, "qvel", 1000, data, text, false, false);
- if (n) {
- mjs_setDouble(key->qvel, data, n);
+ maybe_data = ReadAttrVec(elem, "qvel", false);
+ if (maybe_data.has_value()) {
+ mjs_setDouble(key->qvel, maybe_data->data(), maybe_data->size());
}
// read act
- n = ReadAttr(elem, "act", 1000, data, text, false, false);
- if (n) {
- mjs_setDouble(key->act, data, n);
+ maybe_data = ReadAttrVec(elem, "act", false);
+ if (maybe_data.has_value()) {
+ mjs_setDouble(key->act, maybe_data->data(), maybe_data->size());
}
// read mpos
- n = ReadAttr(elem, "mpos", 1000, data, text, false, false);
- if (n) {
- mjs_setDouble(key->mpos, data, n);
+ maybe_data = ReadAttrVec(elem, "mpos", false);
+ if (maybe_data.has_value()) {
+ mjs_setDouble(key->mpos, maybe_data->data(), maybe_data->size());
}
// read mquat
- n = ReadAttr(elem, "mquat", 1000, data, text, false, false);
- if (n) {
- mjs_setDouble(key->mquat, data, n);
+ maybe_data = ReadAttrVec(elem, "mquat", false);
+ if (maybe_data.has_value()) {
+ mjs_setDouble(key->mquat, maybe_data->data(), maybe_data->size());
}
// read ctrl
- n = ReadAttr(elem, "ctrl", 1000, data, text, false, false);
- if (n) {
- mjs_setDouble(key->ctrl, data, n);
+ maybe_data = ReadAttrVec(elem, "ctrl", false);
+ if (maybe_data.has_value()) {
+ mjs_setDouble(key->ctrl, maybe_data->data(), maybe_data->size());
}
// advance to next element
From 159f23e6b40d3554022bce24ed97283abe373531 Mon Sep 17 00:00:00 2001
From: Alessio Quaglino
Date: Thu, 24 Oct 2024 08:29:03 -0700
Subject: [PATCH 006/426] Remember compiler options during attach.
Note:
- Move compiler options into `mjsCompiler` struct.
- The compiler options from the attached model are not written by mj_SaveXML.
PiperOrigin-RevId: 689391440
Change-Id: I1d63c146a32f87c737b7a55b64a54b0ffe3aecc9
---
doc/APIreference/APItypes.rst | 10 +++
doc/includes/references.h | 19 +++--
include/mujoco/mjspec.h | 21 +++--
introspect/structs.py | 84 ++++++++++--------
.../mujoco/codegen/generate_spec_bindings.py | 8 +-
python/mujoco/raw.h | 1 +
python/mujoco/specs.cc | 1 +
python/mujoco/specs_test.py | 40 ++++-----
src/user/user_api.cc | 11 ++-
src/user/user_flexcomp.cc | 7 +-
src/user/user_flexcomp.h | 2 +-
src/user/user_init.c | 22 ++---
src/user/user_mesh.cc | 4 +-
src/user/user_model.cc | 46 ++++------
src/user/user_objects.cc | 85 +++++++++++++------
src/user/user_objects.h | 3 +-
src/xml/xml.cc | 4 +-
src/xml/xml_native_reader.cc | 38 ++++-----
src/xml/xml_native_writer.cc | 20 ++---
src/xml/xml_urdf.cc | 4 +-
test/user/user_api_test.cc | 48 ++++++++---
test/user/user_model_test.cc | 4 +-
test/user/user_objects_test.cc | 4 +-
unity/Runtime/Bindings/MjBindings.cs | 20 +++++
24 files changed, 302 insertions(+), 204 deletions(-)
diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst
index d9ca5fd3..fba959b2 100644
--- a/doc/APIreference/APItypes.rst
+++ b/doc/APIreference/APItypes.rst
@@ -1140,6 +1140,16 @@ behavior.
.. mujoco-include:: mjsElement
+.. _mjsCompiler:
+
+mjsCompiler
+~~~~~~~~~~
+
+Compiler options.
+
+.. mujoco-include:: mjsCompiler
+
+
.. _mjsBody:
mjsBody
diff --git a/doc/includes/references.h b/doc/includes/references.h
index 9f348881..f1cd64a7 100644
--- a/doc/includes/references.h
+++ b/doc/includes/references.h
@@ -1683,22 +1683,15 @@ typedef enum mjtOrientation_ { // type of orientation specifier
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
+typedef struct mjsCompiler_ { // compiler options
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 eulerseq[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
@@ -1707,6 +1700,16 @@ typedef struct mjSpec_ { // model specification
int inertiagrouprange[2]; // range of geom groups used to compute inertia
int alignfree; // align free joints with inertial frame
mjLROpt LRopt; // options for lengthrange computation
+} mjsCompiler;
+typedef struct mjSpec_ { // model specification
+ mjsElement* element; // element type
+ mjString* modelname; // model name
+
+ // compiler data
+ mjsCompiler compiler; // compiler options
+ mjtByte strippath; // automatically strip paths from mesh files
+ mjString* meshdir; // mesh and hfield directory
+ mjString* texturedir; // texture directory
// engine data
mjOption option; // physics options
diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h
index c6d38c79..58feb8f8 100644
--- a/include/mujoco/mjspec.h
+++ b/include/mujoco/mjspec.h
@@ -121,22 +121,15 @@ typedef struct mjsElement_ { // element type, do not modify
} mjsElement;
-typedef struct mjSpec_ { // model specification
- mjsElement* element; // element type
- mjString* modelname; // model name
-
- // compiler settings
+typedef struct mjsCompiler_ { // compiler options
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 eulerseq[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
@@ -145,6 +138,18 @@ typedef struct mjSpec_ { // model specification
int inertiagrouprange[2]; // range of geom groups used to compute inertia
int alignfree; // align free joints with inertial frame
mjLROpt LRopt; // options for lengthrange computation
+} mjsCompiler;
+
+
+typedef struct mjSpec_ { // model specification
+ mjsElement* element; // element type
+ mjString* modelname; // model name
+
+ // compiler data
+ mjsCompiler compiler; // compiler options
+ mjtByte strippath; // automatically strip paths from mesh files
+ mjString* meshdir; // mesh and hfield directory
+ mjString* texturedir; // texture directory
// engine data
mjOption option; // physics options
diff --git a/introspect/structs.py b/introspect/structs.py
index 449ac230..9f8c8b4c 100644
--- a/introspect/structs.py
+++ b/introspect/structs.py
@@ -8800,25 +8800,11 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
),
)),
- ('mjSpec',
+ ('mjsCompiler',
StructDecl(
- name='mjSpec',
- declname='struct mjSpec_',
+ name='mjsCompiler',
+ declname='struct mjsCompiler_',
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'),
@@ -8844,11 +8830,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
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'),
@@ -8867,20 +8848,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
),
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'),
@@ -8924,6 +8891,51 @@ STRUCTS: Mapping[str, StructDecl] = dict([
type=ValueType(name='mjLROpt'),
doc='options for lengthrange computation',
),
+ ),
+ )),
+ ('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='compiler',
+ type=ValueType(name='mjsCompiler'),
+ doc='compiler options',
+ ),
+ StructFieldDecl(
+ name='strippath',
+ type=ValueType(name='mjtByte'),
+ doc='automatically strip paths from mesh files',
+ ),
+ 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='option',
type=ValueType(name='mjOption'),
diff --git a/python/mujoco/codegen/generate_spec_bindings.py b/python/mujoco/codegen/generate_spec_bindings.py
index e02733c1..700ff883 100644
--- a/python/mujoco/codegen/generate_spec_bindings.py
+++ b/python/mujoco/codegen/generate_spec_bindings.py
@@ -39,8 +39,12 @@ def _value_binding_code(
fullvarname = 'ptr->' + varname
if field.name.startswith('mjs'): # all other mjs are raw structs
fulltype = field.name.replace('mjs', 'raw::Mjs')
- if field.name == 'mjsPlugin' or field.name == 'mjsOrientation':
- fulltype = fulltype + '&' # plugin and orientation are not pointers
+ if (
+ field.name == 'mjsPlugin'
+ or field.name == 'mjsOrientation'
+ or field.name == 'mjsCompiler'
+ ):
+ fulltype = fulltype + '&' # plugin, orientation, compiler are not pointers
else:
fulltype = fulltype + '*'
# non-mjs structs
diff --git a/python/mujoco/raw.h b/python/mujoco/raw.h
index 3d481a12..7776e88c 100644
--- a/python/mujoco/raw.h
+++ b/python/mujoco/raw.h
@@ -58,6 +58,7 @@ using MjsText = ::mjsText;
using MjsTuple = ::mjsTuple;
using MjsKey = ::mjsKey;
using MjsDefault = ::mjsDefault;
+using MjsCompiler = ::mjsCompiler;
using MjOption = ::mjOption;
using MjSolverStat = ::mjSolverStat;
using MjStatistic = ::mjStatistic;
diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc
index b70ac9a7..2359de42 100644
--- a/python/mujoco/specs.cc
+++ b/python/mujoco/specs.cc
@@ -194,6 +194,7 @@ PYBIND11_MODULE(_specs, m) {
py::class_ mjOption(m, "MjOption");
py::class_ mjStatistic(m, "MjStatistic");
py::class_ mjVisual(m, "MjVisual");
+ py::class_ mjsCompiler(m, "MjsCompiler");
DefineArray(m, "MjCharVec");
DefineArray(m, "MjStringVec");
DefineArray(m, "MjByteVec");
diff --git a/python/mujoco/specs_test.py b/python/mujoco/specs_test.py
index e04292cc..b5955e5c 100644
--- a/python/mujoco/specs_test.py
+++ b/python/mujoco/specs_test.py
@@ -34,28 +34,28 @@ class SpecsTest(absltest.TestCase):
spec = mujoco.MjSpec()
# Check that euler sequence order is set correctly.
- self.assertEqual(spec.eulerseq[0], 'x')
- spec.eulerseq = ['z', 'y', 'x']
- self.assertEqual(spec.eulerseq[0], 'z')
+ self.assertEqual(spec.compiler.eulerseq[0], 'x')
+ spec.compiler.eulerseq = ['z', 'y', 'x']
+ self.assertEqual(spec.compiler.eulerseq[0], 'z')
# Change single elements of euler sequence.
- spec.eulerseq[0] = 'y'
- spec.eulerseq[1] = 'z'
- self.assertEqual(spec.eulerseq[0], 'y')
- self.assertEqual(spec.eulerseq[1], 'z')
+ spec.compiler.eulerseq[0] = 'y'
+ spec.compiler.eulerseq[1] = 'z'
+ self.assertEqual(spec.compiler.eulerseq[0], 'y')
+ self.assertEqual(spec.compiler.eulerseq[1], 'z')
# eulerseq is iterable
- self.assertEqual('yzx', ''.join(spec.eulerseq))
+ self.assertEqual('yzx', ''.join(spec.compiler.eulerseq))
# supports `len`
- self.assertLen(spec.eulerseq, 3)
+ self.assertLen(spec.compiler.eulerseq, 3)
# field checks for out-of-bound access on read and on write
with self.assertRaises(IndexError):
- spec.eulerseq[3] = 'x'
+ spec.compiler.eulerseq[3] = 'x'
with self.assertRaises(IndexError):
- spec.eulerseq[-1] = 'x'
+ spec.compiler.eulerseq[-1] = 'x'
# Add a body, check that it has default orientation.
body = spec.worldbody.add_body()
@@ -843,19 +843,15 @@ class SpecsTest(absltest.TestCase):
with self.assertRaises(IndexError):
material.textures[-1] = 'x'
- def test_attach_error(self):
+ def test_attach_units(self):
child = mujoco.MjSpec()
parent = mujoco.MjSpec()
- parent.degree = not child.degree
- body = parent.worldbody.add_body()
- frame = child.worldbody.add_frame()
- with self.assertRaises(ValueError) as cm:
- body.attach_frame(frame, '_', '')
- self.assertEqual(
- str(cm.exception),
- 'Error: cannot attach mjSpecs with incompatible compiler/angle'
- ' attribute',
- )
+ parent.compiler.degree = not child.compiler.degree
+ body = child.worldbody.add_body(euler=[90, 0, 0])
+ frame = parent.worldbody.add_frame(euler=[-mujoco.mjPI / 2, 0, 0])
+ frame.attach_body(body, 'child-', '')
+ model = parent.compile()
+ np.testing.assert_almost_equal(model.body_quat[1], [1, 0, 0, 0])
def test_attach_body_to_site(self):
child = mujoco.MjSpec()
diff --git a/src/user/user_api.cc b/src/user/user_api.cc
index a1412146..6b5a7769 100644
--- a/src/user/user_api.cc
+++ b/src/user/user_api.cc
@@ -69,7 +69,13 @@ mjSpec* mj_makeSpec() {
// copy model
mjSpec* mj_copySpec(const mjSpec* s) {
- mjCModel* modelC = new mjCModel(*static_cast(s->element));
+ mjCModel* modelC = nullptr;
+ try {
+ modelC = new mjCModel(*static_cast(s->element));
+ } catch (mjCError& e) {
+ mju_error("Failed to copy spec: %s", e.message);
+ return nullptr;
+ }
return &modelC->spec;
}
@@ -177,7 +183,8 @@ mjsBody* mjs_attachToSite(mjsSite* parent, const mjsBody* child,
frame->spec.quat[1] = site->spec.quat[1];
frame->spec.quat[2] = site->spec.quat[2];
frame->spec.quat[3] = site->spec.quat[3];
- mjs_resolveOrientation(frame->spec.quat, spec->degree, spec->eulerseq, &site->spec.alt);
+ mjs_resolveOrientation(frame->spec.quat, spec->compiler.degree,
+ spec->compiler.eulerseq, &site->spec.alt);
return mjs_attachBody(&frame->spec, child, prefix, suffix);
}
diff --git a/src/user/user_flexcomp.cc b/src/user/user_flexcomp.cc
index 2f4aa428..732d87ba 100644
--- a/src/user/user_flexcomp.cc
+++ b/src/user/user_flexcomp.cc
@@ -98,8 +98,9 @@ mjCFlexcomp::mjCFlexcomp(void) {
// make flexcomp object
-bool mjCFlexcomp::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) {
- mjCModel* model = (mjCModel*)spec->element;
+bool mjCFlexcomp::Make(mjsBody* body, char* error, int error_sz) {
+ mjCModel* model = static_cast(body->element)->model;
+ mjsCompiler* compiler = static_cast(body->element)->compiler;
mjsFlex* dflex = def.spec.flex;
bool radial = (type == mjFCOMPTYPE_BOX ||
@@ -147,7 +148,7 @@ bool mjCFlexcomp::Make(mjSpec* spec, mjsBody* body, char* error, int error_sz) {
}
// compute orientation
- const char* alterr = mjs_resolveOrientation(quat, model->spec.degree, model->spec.eulerseq, &alt);
+ const char* alterr = mjs_resolveOrientation(quat, compiler->degree, compiler->eulerseq, &alt);
if (alterr) {
return comperr(error, alterr, error_sz);
}
diff --git a/src/user/user_flexcomp.h b/src/user/user_flexcomp.h
index d090022b..92ce884c 100644
--- a/src/user/user_flexcomp.h
+++ b/src/user/user_flexcomp.h
@@ -42,7 +42,7 @@ typedef enum _mjtFcompType {
class mjCFlexcomp {
public:
mjCFlexcomp(void);
- bool Make(mjSpec* spec, mjsBody* body, char* error, int error_sz);
+ bool Make(mjsBody* body, char* error, int error_sz);
bool MakeGrid(char* error, int error_sz);
bool MakeBox(char* error, int error_sz);
diff --git a/src/user/user_init.c b/src/user/user_init.c
index d2ef5798..b5d15413 100644
--- a/src/user/user_init.c
+++ b/src/user/user_init.c
@@ -33,17 +33,17 @@ void mjs_defaultSpec(mjSpec* spec) {
spec->stat.center[0] = mjNAN;
// compiler settings
- spec->autolimits = 1;
- spec->settotalmass = -1;
- spec->degree = 1;
- spec->eulerseq[0] = 'x';
- spec->eulerseq[1] = 'y';
- spec->eulerseq[2] = 'z';
- spec->convexhull = 1;
- spec->usethread = 1;
- spec->inertiafromgeom = mjINERTIAFROMGEOM_AUTO;
- spec->inertiagrouprange[1] = mjNGROUP-1;
- mj_defaultLROpt(&spec->LRopt);
+ spec->compiler.autolimits = 1;
+ spec->compiler.settotalmass = -1;
+ spec->compiler.degree = 1;
+ spec->compiler.eulerseq[0] = 'x';
+ spec->compiler.eulerseq[1] = 'y';
+ spec->compiler.eulerseq[2] = 'z';
+ spec->compiler.convexhull = 1;
+ spec->compiler.usethread = 1;
+ spec->compiler.inertiafromgeom = mjINERTIAFROMGEOM_AUTO;
+ spec->compiler.inertiagrouprange[1] = mjNGROUP-1;
+ mj_defaultLROpt(&spec->compiler.LRopt);
// engine data
mj_defaultOption(&spec->option);
diff --git a/src/user/user_mesh.cc b/src/user/user_mesh.cc
index fbe2383c..d1e20f46 100644
--- a/src/user/user_mesh.cc
+++ b/src/user/user_mesh.cc
@@ -563,7 +563,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
}
// make graph describing convex hull
- if ((model->convexhull && needhull_) || face_.empty()) {
+ if ((model->compiler.convexhull && needhull_) || face_.empty()) {
MakeGraph();
}
@@ -739,7 +739,7 @@ void mjCMesh::FitGeom(mjCGeom* geom, double* meshpos) {
mjuu_copyvec(meshpos, GetPosPtr(geom->typeinertia), 3);
// use inertial box
- if (!model->fitaabb) {
+ if (!model->compiler.fitaabb) {
// get inertia box type (shell or volume)
double* boxsz = GetInertiaBoxPtr(geom->typeinertia);
switch (geom->type) {
diff --git a/src/user/user_model.cc b/src/user/user_model.cc
index 82e3d941..99ab9a11 100644
--- a/src/user/user_model.cc
+++ b/src/user/user_model.cc
@@ -227,8 +227,10 @@ void mjCModel::CopyList(std::vector& dest,
}
// copy the element from the other model to this model
source[i]->ForgetKeyframes();
+ mjSpec* origin = FindSpec(mjs_getString(source[i]->model->spec.modelname));
dest.push_back(candidate);
dest.back()->model = this;
+ dest.back()->compiler = origin ? &origin->compiler : &spec.compiler;
dest.back()->id = -1;
}
if (!dest.empty()) {
@@ -354,20 +356,6 @@ static bool IsPluginActive(
mjCModel& mjCModel::operator+=(const mjCModel& other) {
- // TODO: use compiler settings stored in specs_ during compilation
- std::string msg = "cannot attach mjSpecs with incompatible compiler/";
- if (other.spec.degree != spec.degree) {
- throw mjCError(nullptr, (msg + "angle attribute").c_str());
- }
- if (other.spec.autolimits != spec.autolimits) {
- throw mjCError(nullptr, (msg + "autolimits attribute").c_str());
- }
- if (other.spec.eulerseq[0] != spec.eulerseq[0] ||
- other.spec.eulerseq[1] != spec.eulerseq[1] ||
- other.spec.eulerseq[2] != spec.eulerseq[2]) {
- throw mjCError(nullptr, (msg + "eulerseq attribute").c_str());
- }
-
// create global lists
mjCBody *world = bodies_[0];
ResetTreeLists();
@@ -1784,8 +1772,8 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
mjOption saveopt = m->opt;
m->opt.disableflags = mjDSBL_FRICTIONLOSS | mjDSBL_CONTACT | mjDSBL_PASSIVE |
mjDSBL_GRAVITY | mjDSBL_ACTUATION;
- if (LRopt.timestep>0) {
- m->opt.timestep = LRopt.timestep;
+ if (compiler.LRopt.timestep>0) {
+ m->opt.timestep = compiler.LRopt.timestep;
}
// number of threads available
@@ -1800,14 +1788,14 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
m->actuator_biastype[i]==mjBIAS_MUSCLE);
int isuser = (m->actuator_gaintype[i]==mjGAIN_USER ||
m->actuator_biastype[i]==mjBIAS_USER);
- if ((LRopt.mode==mjLRMODE_NONE) ||
- (LRopt.mode==mjLRMODE_MUSCLE && !ismuscle) ||
- (LRopt.mode==mjLRMODE_MUSCLEUSER && !ismuscle && !isuser)) {
+ if ((compiler.LRopt.mode==mjLRMODE_NONE) ||
+ (compiler.LRopt.mode==mjLRMODE_MUSCLE && !ismuscle) ||
+ (compiler.LRopt.mode==mjLRMODE_MUSCLEUSER && !ismuscle && !isuser)) {
continue;
}
// use existing length range if available
- if (LRopt.useexisting &&
+ if (compiler.LRopt.useexisting &&
(m->actuator_lengthrange[2*i] < m->actuator_lengthrange[2*i+1])) {
continue;
}
@@ -1817,10 +1805,10 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
}
// single thread
- if (!usethread || cnt<2 || nthread<2) {
+ if (!compiler.usethread || cnt<2 || nthread<2) {
char err[200];
for (int i=0; inu; i++) {
- if (!mj_setLengthRange(m, data, i, &LRopt, err, 200)) {
+ if (!mj_setLengthRange(m, data, i, &compiler.LRopt, err, 200)) {
throw mjCError(0, "%s", err);
}
}
@@ -1844,7 +1832,7 @@ void mjCModel::LengthRange(mjModel* m, mjData* data) {
// prepare thread function arguments, clear errors
LRThreadArg arg[kMaxCompilerThreads];
for (int i=0; i 1) {
+ if (compiler.usethread && meshes_.size() > 1) {
// multi-threaded mesh compile
CompileMeshes(vfs);
} else {
@@ -3981,10 +3969,10 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
reassignid(excludes_);
// resolve asset references, compute sizes
- IndexAssets(discardvisual);
+ IndexAssets(compiler.discardvisual);
SetSizes();
// fuse static if enabled
- if (fusestatic) {
+ if (compiler.fusestatic) {
FuseStatic();
}
@@ -4144,8 +4132,8 @@ void mjCModel::TryCompile(mjModel*& m, mjData*& d, const mjVFS* vfs) {
CopyObjects(m);
// scale mass
- if (settotalmass>0) {
- mj_setTotalmass(m, settotalmass);
+ if (compiler.settotalmass>0) {
+ mj_setTotalmass(m, compiler.settotalmass);
}
// set arena size into m->narena
diff --git a/src/user/user_objects.cc b/src/user/user_objects.cc
index 4c084b22..e795f49a 100644
--- a/src/user/user_objects.cc
+++ b/src/user/user_objects.cc
@@ -749,6 +749,7 @@ void mjCBase::SetFrame(mjCFrame* _frame) {
mjCBody::mjCBody(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
mjs_defaultBody(&spec);
elemtype = mjOBJ_BODY;
@@ -785,6 +786,8 @@ mjCBody::mjCBody(mjCModel* _model) {
mjCBody::mjCBody(const mjCBody& other, mjCModel* _model) {
model = _model;
+ mjSpec* origin = model->FindSpec(mjs_getString(other.model->spec.modelname));
+ compiler = origin ? &origin->compiler : &model->spec.compiler;
*this = other;
}
@@ -868,9 +871,11 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
}
// copy input frame
+ mjSpec* origin = model->FindSpec(mjs_getString(other.model->spec.modelname));
frames.push_back(new mjCFrame(other));
frames.back()->body = this;
frames.back()->model = model;
+ frames.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
frames.back()->frame = other.frame;
frames.back()->NameSpace(other.model);
int i = frames.size();
@@ -931,9 +936,11 @@ void mjCBody::CopyList(std::vector& dst, const std::vector& src,
if (pframe && !pframe->IsAncestor(src[i]->frame)) {
continue; // skip if the element is not inside pframe
}
+ mjSpec* origin = model->FindSpec(mjs_getString(src[i]->model->spec.modelname));
dst.push_back(new T(*src[i]));
dst.back()->body = this;
dst.back()->model = model;
+ dst.back()->compiler = origin ? &origin->compiler : &model->spec.compiler;
dst.back()->id = -1;
dst.back()->classname = src[i]->classname;
@@ -1413,8 +1420,8 @@ void mjCBody::InertiaFromGeom(void) {
// select geoms based on group
sel.clear();
for (int i=0; igroup>=model->inertiagrouprange[0] &&
- geoms[i]->group<=model->inertiagrouprange[1]) {
+ if (geoms[i]->group>=compiler->inertiagrouprange[0] &&
+ geoms[i]->group<=compiler->inertiagrouprange[1]) {
sel.push_back(geoms[i]);
}
}
@@ -1559,7 +1566,7 @@ void mjCBody::Compile(void) {
// check and process orientation alternatives for body
if (alt.type != mjORIENTATION_QUAT) {
- const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt);
+ const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) {
throw mjCError(this, "error '%s' in frame alternative", err);
}
@@ -1582,7 +1589,7 @@ void mjCBody::Compile(void) {
}
if (ialt.type != mjORIENTATION_QUAT) {
- const char* err = ResolveOrientation(iquat, model->degree, model->eulerseq, ialt);
+ const char* err = ResolveOrientation(iquat, compiler->degree, compiler->eulerseq, ialt);
if (err) {
throw mjCError(this, "error '%s' in inertia alternative", err);
}
@@ -1591,15 +1598,15 @@ void mjCBody::Compile(void) {
// compile all geoms
for (int i=0; iinferinertia = id>0 &&
- (!explicitinertial || model->inertiafromgeom == mjINERTIAFROMGEOM_TRUE) &&
- geoms[i]->spec.group >= model->inertiagrouprange[0] &&
- geoms[i]->spec.group <= model->inertiagrouprange[1];
+ (!explicitinertial || compiler->inertiafromgeom == mjINERTIAFROMGEOM_TRUE) &&
+ geoms[i]->spec.group >= compiler->inertiagrouprange[0] &&
+ geoms[i]->spec.group <= compiler->inertiagrouprange[1];
geoms[i]->Compile();
}
// set inertial frame from geoms if necessary
- if (id>0 && (model->inertiafromgeom==mjINERTIAFROMGEOM_TRUE ||
- (!mjuu_defined(ipos[0]) && model->inertiafromgeom==mjINERTIAFROMGEOM_AUTO))) {
+ if (id>0 && (compiler->inertiafromgeom==mjINERTIAFROMGEOM_TRUE ||
+ (!mjuu_defined(ipos[0]) && compiler->inertiafromgeom==mjINERTIAFROMGEOM_AUTO))) {
InertiaFromGeom();
}
@@ -1612,10 +1619,10 @@ void mjCBody::Compile(void) {
// check and correct mass and inertia
if (id>0) {
// fix minimum
- mass = std::max(mass, model->boundmass);
- inertia[0] = std::max(inertia[0], model->boundinertia);
- inertia[1] = std::max(inertia[1], model->boundinertia);
- inertia[2] = std::max(inertia[2], model->boundinertia);
+ mass = std::max(mass, compiler->boundmass);
+ inertia[0] = std::max(inertia[0], compiler->boundinertia);
+ inertia[1] = std::max(inertia[1], compiler->boundinertia);
+ inertia[2] = std::max(inertia[2], compiler->boundinertia);
// check for negative values
if (mass<0 || inertia[0]<0 || inertia[1]<0 ||inertia[2]<0) {
@@ -1626,7 +1633,7 @@ void mjCBody::Compile(void) {
if (inertia[0] + inertia[1] < inertia[2] ||
inertia[0] + inertia[2] < inertia[1] ||
inertia[1] + inertia[2] < inertia[0]) {
- if (model->balanceinertia) {
+ if (compiler->balanceinertia) {
inertia[0] = inertia[1] = inertia[2] = (inertia[0] + inertia[1] + inertia[2])/3.0;
} else {
throw mjCError(this, "inertia must satisfy A + B >= C; use 'balanceinertia' to fix");
@@ -1654,7 +1661,7 @@ void mjCBody::Compile(void) {
bodies.empty() && // no child bodies AND
(joints[0]->spec.align == 1 || // either joint.align="true"
(joints[0]->spec.align == 2 && // or joint.align="auto"
- model->alignfree))); // and compiler.align="true"
+ compiler->alignfree))); // and compiler->align="true"
// free-joint alignment, phase 1 (this body + child geoms)
double ipos_inverse[3], iquat_inverse[4];
@@ -1740,7 +1747,7 @@ void mjCBody::Compile(void) {
}
// if discarding visual geoms, use explicit inertias
- if (model->discardvisual) {
+ if (compiler->discardvisual) {
for (int j=0; jIsVisual()) {
explicitinertial = true;
@@ -1782,6 +1789,7 @@ mjCFrame::mjCFrame(mjCModel* _model, mjCFrame* _frame) {
elemtype = mjOBJ_FRAME;
compiled = false;
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
body = NULL;
frame = _frame ? _frame : NULL;
last_attached = nullptr;
@@ -1905,7 +1913,7 @@ void mjCFrame::Compile() {
}
CopyFromSpec();
- const char* err = ResolveOrientation(quat, model->spec.degree, model->spec.eulerseq, alt);
+ const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) {
throw mjCError(this, "orientation specification error '%s' in site %d", err, id);
}
@@ -1940,6 +1948,7 @@ mjCJoint::mjCJoint(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// point to local
@@ -2069,7 +2078,7 @@ int mjCJoint::Compile(void) {
// otherwise if limited is auto, check consistency wrt auto-limits
else if (limited == mjLIMITED_AUTO) {
bool hasrange = !(range[0]==0 && range[1]==0);
- checklimited(this, model->autolimits, "joint", "", limited, hasrange);
+ checklimited(this, compiler->autolimits, "joint", "", limited, hasrange);
}
// resolve limits
@@ -2083,7 +2092,7 @@ int mjCJoint::Compile(void) {
}
// convert limits to radians
- if (model->degree && (type==mjJNT_HINGE || type==mjJNT_BALL)) {
+ if (compiler->degree && (type==mjJNT_HINGE || type==mjJNT_BALL)) {
if (range[0]) {
range[0] *= mjPI/180.0;
}
@@ -2100,7 +2109,7 @@ int mjCJoint::Compile(void) {
// otherwise if actfrclimited is auto, check consistency wrt auto-limits
else if (actfrclimited == mjLIMITED_AUTO) {
bool hasrange = !(actfrcrange[0]==0 && actfrcrange[1]==0);
- checklimited(this, model->autolimits, "joint", "", actfrclimited, hasrange);
+ checklimited(this, compiler->autolimits, "joint", "", actfrclimited, hasrange);
}
// resolve actuator force range limits
@@ -2144,7 +2153,7 @@ int mjCJoint::Compile(void) {
}
// convert reference angles to radians for hinge joints
- if (type==mjJNT_HINGE && model->degree) {
+ if (type==mjJNT_HINGE && compiler->degree) {
ref *= mjPI/180.0;
springref *= mjPI/180.0;
}
@@ -2193,6 +2202,7 @@ mjCGeom::mjCGeom(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// point to local
@@ -2892,7 +2902,7 @@ void mjCGeom::Compile(void) {
// not 'fromto': try alternative
else {
- const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt);
+ const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) {
throw mjCError(this, "orientation specification error '%s' in geom %d", err, id);
}
@@ -3034,6 +3044,7 @@ mjCSite::mjCSite(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
}
@@ -3140,7 +3151,7 @@ void mjCSite::Compile(void) {
// alternative orientation
else {
- const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt);
+ const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) {
throw mjCError(this, "orientation specification error '%s' in site %d", err, id);
}
@@ -3179,6 +3190,7 @@ mjCCamera::mjCCamera(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// point to local
@@ -3248,7 +3260,7 @@ void mjCCamera::Compile(void) {
userdata_.resize(model->nuser_cam);
// process orientation specifications
- const char* err = ResolveOrientation(quat, model->degree, model->eulerseq, alt);
+ const char* err = ResolveOrientation(quat, compiler->degree, compiler->eulerseq, alt);
if (err) {
throw mjCError(this, "orientation specification error '%s' in camera %d", err, id);
}
@@ -3330,6 +3342,7 @@ mjCLight::mjCLight(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
PointToLocal();
@@ -3423,6 +3436,7 @@ mjCHField::mjCHField(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear variables
data.clear();
@@ -3678,6 +3692,7 @@ mjCTexture::mjCTexture(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear user settings: single file
spec_file_.clear();
@@ -4511,6 +4526,7 @@ mjCMaterial::mjCMaterial(mjCModel* _model, mjCDef* _def) {
}
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
PointToLocal();
@@ -4597,6 +4613,7 @@ mjCPair::mjCPair(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// point to local
@@ -4817,6 +4834,7 @@ void mjCPair::Compile(void) {
mjCBodyPair::mjCBodyPair(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
elemtype = mjOBJ_EXCLUDE;
// set defaults
@@ -4955,6 +4973,7 @@ mjCEquality::mjCEquality(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// point to local
@@ -5116,6 +5135,7 @@ mjCTendon::mjCTendon(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// point to local
@@ -5201,6 +5221,7 @@ mjCTendon::~mjCTendon() {
void mjCTendon::SetModel(mjCModel* _model) {
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
for (int i=0; imodel = _model;
}
@@ -5423,7 +5444,7 @@ void mjCTendon::Compile(void) {
// if limited is auto, set to 1 if range is specified, otherwise unlimited
if (limited == mjLIMITED_AUTO) {
bool hasrange = !(range[0]==0 && range[1]==0);
- checklimited(this, model->autolimits, "tendon", "", limited, hasrange);
+ checklimited(this, compiler->autolimits, "tendon", "", limited, hasrange);
}
// check limits
@@ -5447,6 +5468,7 @@ mjCWrap::mjCWrap(mjCModel* _model, mjCTendon* _tendon) {
// set model and tendon pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
tendon = _tendon;
// clear variables
@@ -5591,6 +5613,7 @@ mjCActuator::mjCActuator(mjCModel* _model, mjCDef* _def) {
// set model, def
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
classname = _def ? _def->name : "main";
// in case this actuator is not compiled
@@ -5846,15 +5869,15 @@ void mjCActuator::Compile(void) {
// if limited is auto, check for inconsistency wrt to autolimits
if (forcelimited == mjLIMITED_AUTO) {
bool hasrange = !(forcerange[0]==0 && forcerange[1]==0);
- checklimited(this, model->autolimits, "actuator", "force", forcelimited, hasrange);
+ checklimited(this, compiler->autolimits, "actuator", "force", forcelimited, hasrange);
}
if (ctrllimited == mjLIMITED_AUTO) {
bool hasrange = !(ctrlrange[0]==0 && ctrlrange[1]==0);
- checklimited(this, model->autolimits, "actuator", "ctrl", ctrllimited, hasrange);
+ checklimited(this, compiler->autolimits, "actuator", "ctrl", ctrllimited, hasrange);
}
if (actlimited == mjLIMITED_AUTO) {
bool hasrange = !(actrange[0]==0 && actrange[1]==0);
- checklimited(this, model->autolimits, "actuator", "act", actlimited, hasrange);
+ checklimited(this, compiler->autolimits, "actuator", "act", actlimited, hasrange);
}
// check limits
@@ -5951,6 +5974,7 @@ mjCSensor::mjCSensor(mjCModel* _model) {
// set model
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear private variables
spec_objname_.clear();
@@ -6477,6 +6501,7 @@ mjCNumeric::mjCNumeric(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear variables
spec_data_.clear();
@@ -6566,6 +6591,7 @@ mjCText::mjCText(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear variables
spec_data_.clear();
@@ -6643,6 +6669,7 @@ mjCTuple::mjCTuple(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear variables
spec_objtype_.clear();
@@ -6776,6 +6803,7 @@ mjCKey::mjCKey(mjCModel* _model) {
// set model pointer
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
// clear variables
spec_qpos_.clear();
@@ -6952,6 +6980,7 @@ mjCPlugin::mjCPlugin(mjCModel* _model) {
plugin_slot = -1;
parent = this;
model = _model;
+ if (_model) compiler = &_model->spec.compiler;
name.clear();
plugin_name.clear();
diff --git a/src/user/user_objects.h b/src/user/user_objects.h
index 85db8da8..1018fb8b 100644
--- a/src/user/user_objects.h
+++ b/src/user/user_objects.h
@@ -212,7 +212,8 @@ class mjCBase : public mjCBase_ {
mjCBase& operator=(const mjCBase& other);
mjCFrame* frame; // pointer to frame transformation
- mjCModel* model; // pointer to model that created object
+ mjCModel* model; // pointer to model that owns object
+ mjsCompiler* compiler; // pointer to the compiler options
virtual ~mjCBase() = default; // destructor
diff --git a/src/xml/xml.cc b/src/xml/xml.cc
index abcd8c13..5277a534 100644
--- a/src/xml/xml.cc
+++ b/src/xml/xml.cc
@@ -365,8 +365,8 @@ mjSpec* ParseXML(const char* filename, const mjVFS* vfs,
// set reasonable default for parsing a URDF
// this is separate from the Parser to allow multiple URDFs to be loaded.
spec->strippath = true;
- spec->fusestatic = true;
- spec->discardvisual = true;
+ spec->compiler.fusestatic = true;
+ spec->compiler.discardvisual = true;
parser.SetModel(spec);
parser.Parse(root);
diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc
index f145afbd..854459cb 100644
--- a/src/xml/xml_native_reader.cc
+++ b/src/xml/xml_native_reader.cc
@@ -972,19 +972,19 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
// top-level attributes
if (MapValue(section, "autolimits", &n, bool_map, 2)) {
- spec->autolimits = (n==1);
+ spec->compiler.autolimits = (n==1);
}
- ReadAttr(section, "boundmass", 1, &spec->boundmass, text);
- ReadAttr(section, "boundinertia", 1, &spec->boundinertia, text);
- ReadAttr(section, "settotalmass", 1, &spec->settotalmass, text);
+ ReadAttr(section, "boundmass", 1, &spec->compiler.boundmass, text);
+ ReadAttr(section, "boundinertia", 1, &spec->compiler.boundinertia, text);
+ ReadAttr(section, "settotalmass", 1, &spec->compiler.settotalmass, text);
if (MapValue(section, "balanceinertia", &n, bool_map, 2)) {
- spec->balanceinertia = (n==1);
+ spec->compiler.balanceinertia = (n==1);
}
if (MapValue(section, "strippath", &n, bool_map, 2)) {
spec->strippath = (n==1);
}
if (MapValue(section, "fitaabb", &n, bool_map, 2)) {
- spec->fitaabb = (n==1);
+ spec->compiler.fitaabb = (n==1);
}
if (MapValue(section, "coordinate", &n, coordinate_map, 2)) {
if (n==1) {
@@ -993,13 +993,13 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
}
}
if (MapValue(section, "angle", &n, angle_map, 2)) {
- spec->degree = (n==1);
+ spec->compiler.degree = (n==1);
}
if (ReadAttrTxt(section, "eulerseq", text)) {
if (text.size()!=3) {
throw mjXError(section, "euler format must have length 3");
}
- memcpy(spec->eulerseq, text.c_str(), 3);
+ memcpy(spec->compiler.eulerseq, text.c_str(), 3);
}
if (ReadAttrTxt(section, "assetdir", text)) {
mjs_setString(spec->meshdir, text.c_str());
@@ -1014,27 +1014,27 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
mjs_setString(spec->texturedir, texturedir.c_str());
}
if (MapValue(section, "discardvisual", &n, bool_map, 2)) {
- spec->discardvisual = (n==1);
+ spec->compiler.discardvisual = (n==1);
}
if (MapValue(section, "convexhull", &n, bool_map, 2)) {
- spec->convexhull = (n==1);
+ spec->compiler.convexhull = (n==1);
}
if (MapValue(section, "usethread", &n, bool_map, 2)) {
- spec->usethread = (n==1);
+ spec->compiler.usethread = (n==1);
}
if (MapValue(section, "fusestatic", &n, bool_map, 2)) {
- spec->fusestatic = (n==1);
+ spec->compiler.fusestatic = (n==1);
}
- MapValue(section, "inertiafromgeom", &spec->inertiafromgeom, TFAuto_map, 3);
- ReadAttr(section, "inertiagrouprange", 2, spec->inertiagrouprange, text);
+ MapValue(section, "inertiafromgeom", &spec->compiler.inertiafromgeom, TFAuto_map, 3);
+ ReadAttr(section, "inertiagrouprange", 2, spec->compiler.inertiagrouprange, text);
if (MapValue(section, "alignfree", &n, bool_map, 2)) {
- spec->alignfree = (n==1);
+ spec->compiler.alignfree = (n==1);
}
// lengthrange subelement
XMLElement* elem = FindSubElem(section, "lengthrange");
if (elem) {
- mjLROpt* opt = &(spec->LRopt);
+ mjLROpt* opt = &(spec->compiler.LRopt);
// flags
MapValue(elem, "mode", &opt->mode, lrmode_map, lrmode_sz);
@@ -2759,7 +2759,7 @@ void mjXReader::OneFlexcomp(XMLElement* elem, mjsBody* body, const mjVFS* vfs) {
// make flexcomp
char error[200];
- bool res = fcomp.Make(spec, body, error, 200);
+ bool res = fcomp.Make(body, error, 200);
// throw error
if (!res) {
@@ -3575,7 +3575,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
alt.type = mjORIENTATION_EULER;
mjuu_copyvec(alt.euler, euler, 3);
double rotation[4] = {1, 0, 0, 0};
- mjs_resolveOrientation(rotation, spec->degree, spec->eulerseq, &alt);
+ mjs_resolveOrientation(rotation, spec->compiler.degree, spec->compiler.eulerseq, &alt);
// read childdef
mjsDefault* childdef = 0;
@@ -3610,7 +3610,7 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
alt.euler[0] = i*euler[0];
alt.euler[1] = i*euler[1];
alt.euler[2] = i*euler[2];
- mjs_resolveOrientation(quat, spec->degree, spec->eulerseq, &alt);
+ mjs_resolveOrientation(quat, spec->compiler.degree, spec->compiler.eulerseq, &alt);
mjuu_setvec(pframe->quat, quat[0], quat[1], quat[2], quat[3]);
// process suffix
diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc
index 02c265a3..907920a6 100644
--- a/src/xml/xml_native_writer.cc
+++ b/src/xml/xml_native_writer.cc
@@ -920,8 +920,8 @@ void mjXWriter::Compiler(XMLElement* root) {
XMLElement* section = InsertEnd(root, "compiler");
// settings
- if (!model->convexhull) {
- WriteAttrTxt(section, "convexhull", FindValue(bool_map, 2, model->convexhull));
+ if (!model->compiler.convexhull) {
+ WriteAttrTxt(section, "convexhull", FindValue(bool_map, 2, model->compiler.convexhull));
}
WriteAttrTxt(section, "angle", "radian");
if (!model->get_meshdir().empty()) {
@@ -930,20 +930,20 @@ void mjXWriter::Compiler(XMLElement* root) {
if (!model->get_texturedir().empty()) {
WriteAttrTxt(section, "texturedir", model->get_texturedir());
}
- if (!model->usethread) {
+ if (!model->compiler.usethread) {
WriteAttrTxt(section, "usethread", "false");
}
- if (model->boundmass) {
- WriteAttr(section, "boundmass", 1, &model->boundmass);
+ if (model->compiler.boundmass) {
+ WriteAttr(section, "boundmass", 1, &model->compiler.boundmass);
}
- if (model->boundinertia) {
- WriteAttr(section, "boundinertia", 1, &model->boundinertia);
+ if (model->compiler.boundinertia) {
+ WriteAttr(section, "boundinertia", 1, &model->compiler.boundinertia);
}
- if (model->alignfree) {
+ if (model->compiler.alignfree) {
WriteAttrTxt(section, "alignfree", "true");
}
- if (!model->autolimits) {
+ if (!model->compiler.autolimits) {
WriteAttrTxt(section, "autolimits", "false");
}
}
@@ -1611,7 +1611,7 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame, string_vi
WriteVector(elem, "user", body->get_userdata());
// write inertial
- if (body->explicitinertial && model->inertiafromgeom!=mjINERTIAFROMGEOM_TRUE) {
+ if (body->explicitinertial && model->compiler.inertiafromgeom!=mjINERTIAFROMGEOM_TRUE) {
XMLElement* inertial = InsertEnd(elem, "inertial");
WriteAttr(inertial, "pos", 3, body->ipos);
WriteAttr(inertial, "quat", 4, body->iquat, unitq);
diff --git a/src/xml/xml_urdf.cc b/src/xml/xml_urdf.cc
index 3dc98b65..301ac22a 100644
--- a/src/xml/xml_urdf.cc
+++ b/src/xml/xml_urdf.cc
@@ -109,7 +109,7 @@ void mjXURDF::Parse(
}
// enforce required compiler defaults for URDF
- spec->degree = false;
+ spec->compiler.degree = false;
// get model name
std::string modelname;
@@ -316,7 +316,7 @@ void mjXURDF::Body(XMLElement* body_elem) {
}
}
// create geom if not discarded
- if (!spec->discardvisual) {
+ if (!spec->compiler.discardvisual) {
pgeom = Geom(elem, pbody, false);
// save color
diff --git a/test/user/user_api_test.cc b/test/user/user_api_test.cc
index 7922d1d8..ab824637 100644
--- a/test/user/user_api_test.cc
+++ b/test/user/user_api_test.cc
@@ -1839,23 +1839,43 @@ TEST_F(MujocoTest, RepeatedAttachKeyframe) {
mj_deleteModel(model_2);
}
-TEST_F(MujocoTest, DifferentUnitsNotAllowed) {
- mjSpec* spec_1 = mj_makeSpec();
- mjSpec* spec_2 = mj_makeSpec();
- spec_1->degree = 1;
- spec_2->degree = 0;
+TEST_F(MujocoTest, DifferentUnitsAllowed) {
+ mjSpec* child = mj_makeSpec();
+ child->compiler.degree = 1;
+ mjsBody* body = mjs_addBody(mjs_findBody(child, "world"), 0);
+ body->alt.type = mjORIENTATION_EULER;
+ body->alt.euler[0] = 90;
- mjsBody* body = mjs_addBody(mjs_findBody(spec_1, "world"), 0);
- mjsFrame* frame = mjs_addFrame(mjs_findBody(spec_2, "world"), 0);
+ mjSpec* parent = mj_makeSpec();
+ parent->compiler.degree = 0;
+ mjsFrame* frame = mjs_addFrame(mjs_findBody(parent, "world"), 0);
+ frame->alt.type = mjORIENTATION_EULER;
+ frame->alt.euler[0] = -mjPI / 2;
- constexpr char msg[] = "mjSpecs with incompatible compiler/angle";
- EXPECT_THAT(mjs_attachBody(frame, body, "child-", ""), IsNull());
- EXPECT_THAT(mjs_attachFrame(body, frame, "child-", ""), IsNull());
- EXPECT_THAT(mjs_getError(spec_1), HasSubstr(msg));
- EXPECT_THAT(mjs_getError(spec_2), HasSubstr(msg));
+ EXPECT_THAT(mjs_attachBody(frame, body, "child-", ""), NotNull());
+ mjModel* model = mj_compile(parent, 0);
+ EXPECT_THAT(model, NotNull());
+ EXPECT_NEAR(model->body_quat[4], 1, 1e-12);
+ EXPECT_NEAR(model->body_quat[5], 0, 1e-12);
+ EXPECT_NEAR(model->body_quat[6], 0, 1e-12);
+ EXPECT_NEAR(model->body_quat[7], 0, 1e-12);
- mj_deleteSpec(spec_1);
- mj_deleteSpec(spec_2);
+ mjSpec* copy = mj_copySpec(parent);
+ EXPECT_THAT(copy, NotNull());
+ mj_deleteModel(model);
+ mj_deleteSpec(child);
+ mj_deleteSpec(parent);
+
+ // check that deleting `parent` or `child` does not invalidate the copy
+ mjModel* copy_model = mj_compile(copy, 0);
+ EXPECT_THAT(copy_model, NotNull());
+ EXPECT_NEAR(copy_model->body_quat[0], 1, 1e-12);
+ EXPECT_NEAR(copy_model->body_quat[1], 0, 1e-12);
+ EXPECT_NEAR(copy_model->body_quat[2], 0, 1e-12);
+ EXPECT_NEAR(copy_model->body_quat[3], 0, 1e-12);
+
+ mj_deleteModel(copy_model);
+ mj_deleteSpec(copy);
}
TEST_F(MujocoTest, CopyAttachedSpec) {
diff --git a/test/user/user_model_test.cc b/test/user/user_model_test.cc
index 74a14f77..24467d97 100644
--- a/test/user/user_model_test.cc
+++ b/test/user/user_model_test.cc
@@ -480,8 +480,8 @@ TEST_F(LengthRangeTest, LengthRangeThreading) {
DoubleNear(std::sqrt(5.0), 1e-3));
// recompile without threads
- ASSERT_EQ(spec->usethread, 1);
- spec->usethread = 0;
+ ASSERT_EQ(spec->compiler.usethread, 1);
+ spec->compiler.usethread = 0;
mjModel* model2 = mj_compile(spec, 0);
EXPECT_THAT(model2, NotNull()) << error;
diff --git a/test/user/user_objects_test.cc b/test/user/user_objects_test.cc
index 2a97ec58..5d6891c3 100644
--- a/test/user/user_objects_test.cc
+++ b/test/user/user_objects_test.cc
@@ -1176,7 +1176,7 @@ TEST_F(MjCJointTest, AlignFree) {
std::array err;
mjSpec* s = mj_parseXML(xml_path.c_str(), nullptr, err.data(), err.size());
ASSERT_THAT(s, NotNull()) << err.data();
- s->alignfree = 1; // auto-aligned free joint
+ s->compiler.alignfree = 1; // auto-aligned free joint
mjModel* m = mj_compile(s, nullptr);
ASSERT_THAT(m, NotNull());
@@ -1186,7 +1186,7 @@ TEST_F(MjCJointTest, AlignFree) {
EXPECT_EQ(m->dof_simplenum[0], 6);
// make unaligned model
- s->alignfree = 0; // unaligned free joint
+ s->compiler.alignfree = 0; // unaligned free joint
mjModel* m_u = mj_compile(s, nullptr);
ASSERT_THAT(m_u, NotNull());
diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs
index 9bd3c6bb..dbc5364b 100644
--- a/unity/Runtime/Bindings/MjBindings.cs
+++ b/unity/Runtime/Bindings/MjBindings.cs
@@ -5716,6 +5716,26 @@ public unsafe struct mjrContext_ {
public int readDepthMap;
}
+[StructLayout(LayoutKind.Sequential)]
+public unsafe struct mjsCompiler_ {
+ public byte autolimits;
+ public double boundmass;
+ public double boundinertia;
+ public double settotalmass;
+ public byte balanceinertia;
+ public byte fitaabb;
+ public byte degree;
+ public fixed sbyte eulerseq[3];
+ public byte discardvisual;
+ public byte convexhull;
+ public byte usethread;
+ public byte fusestatic;
+ public int inertiafromgeom;
+ public fixed int inertiagrouprange[2];
+ public int alignfree;
+ public mjLROpt_ LRopt;
+}
+
[StructLayout(LayoutKind.Sequential)]
public unsafe struct mjuiState_ {
public int nrect;
From a3ea01e57e07750b83b54c1387c4c3dc61930030 Mon Sep 17 00:00:00 2001
From: Yuval Tassa
Date: Thu, 24 Oct 2024 09:14:13 -0700
Subject: [PATCH 007/426] Remove deprecated `mju_rotVecMat` and
`mju_rotVecMatT` functions.
PiperOrigin-RevId: 689405782
Change-Id: I0281376d5d6f5c31ea2603d61c03c7b94aaa6312
---
doc/APIreference/functions.rst | 18 ---------
doc/changelog.rst | 3 +-
doc/includes/references.h | 2 -
include/mujoco/mujoco.h | 6 ---
introspect/functions.py | 58 ----------------------------
python/mujoco/functions.cc | 2 -
src/engine/engine_util_blas.c | 14 -------
src/engine/engine_util_blas.h | 6 ---
unity/Runtime/Bindings/MjBindings.cs | 6 ---
9 files changed, 2 insertions(+), 113 deletions(-)
diff --git a/doc/APIreference/functions.rst b/doc/APIreference/functions.rst
index 0ce805ce..38568b35 100644
--- a/doc/APIreference/functions.rst
+++ b/doc/APIreference/functions.rst
@@ -3142,24 +3142,6 @@ mju_mulMatTVec3
Multiply transposed 3-by-3 matrix by vector: res = mat' * vec.
-.. _mju_rotVecMat:
-
-mju_rotVecMat
-~~~~~~~~~~~~~
-
-.. mujoco-include:: mju_rotVecMat
-
-Deprecated, use mju_mulMatVec3(res, mat, vec).
-
-.. _mju_rotVecMatT:
-
-mju_rotVecMatT
-~~~~~~~~~~~~~~
-
-.. mujoco-include:: mju_rotVecMatT
-
-Deprecated, use mju_mulMatTVec3(res, mat, vec).
-
.. _mju_cross:
mju_cross
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 4cb748dd..8f69bb07 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -9,6 +9,7 @@ General
^^^^^^^
- The global compiler flag ``exactmeshinertia`` has been removed and replaced with the mesh-specific
:ref:`inertia` attribute.
+- Removed the deprecated ``mju_rotVecMat`` and ``mju_rotVecMatT`` functions.
MJX
^^^
@@ -214,7 +215,7 @@ General
3. Calls to :ref:`mj_defaultVFS` may allocate memory inside VFS, and the corresponding
:ref:`mj_deleteVFS` must be called to deallocate any internal allocated memory.
- 4. Deprecated :ref:`mju_rotVecMat` and :ref:`mju_rotVecMatT` in favor of :ref:`mju_mulMatVec3` and
+ 4. Deprecated ``mju_rotVecMat`` and ``mju_rotVecMatT`` in favor of :ref:`mju_mulMatVec3` and
:ref:`mju_mulMatTVec3`. These function names and argument order are more consistent with the rest of the API.
The older functions have been removed from the Python bindings and will be removed from the C API in the next
release.
diff --git a/doc/includes/references.h b/doc/includes/references.h
index f1cd64a7..e19503d4 100644
--- a/doc/includes/references.h
+++ b/doc/includes/references.h
@@ -3432,8 +3432,6 @@ mjtNum mju_dot3(const mjtNum vec1[3], const mjtNum vec2[3]);
mjtNum mju_dist3(const mjtNum pos1[3], const mjtNum pos2[3]);
void mju_mulMatVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
void mju_mulMatTVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
-void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
-void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]);
void mju_zero4(mjtNum res[4]);
void mju_unit4(mjtNum res[4]);
diff --git a/include/mujoco/mujoco.h b/include/mujoco/mujoco.h
index 751c6cdf..849462fe 100644
--- a/include/mujoco/mujoco.h
+++ b/include/mujoco/mujoco.h
@@ -975,12 +975,6 @@ MJAPI void mju_mulMatVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3
// Multiply transposed 3-by-3 matrix by vector: res = mat' * vec.
MJAPI void mju_mulMatTVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
-// Deprecated, use mju_mulMatVec3(res, mat, vec).
-MJAPI void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
-
-// Deprecated, use mju_mulMatTVec3(res, mat, vec).
-MJAPI void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
-
// Compute cross-product: res = cross(a, b).
MJAPI void mju_cross(mjtNum res[3], const mjtNum a[3], const mjtNum b[3]);
diff --git a/introspect/functions.py b/introspect/functions.py
index c0a3a196..4f86485d 100644
--- a/introspect/functions.py
+++ b/introspect/functions.py
@@ -6187,64 +6187,6 @@ FUNCTIONS: Mapping[str, FunctionDecl] = dict([
),
doc="Multiply transposed 3-by-3 matrix by vector: res = mat' * vec.",
)),
- ('mju_rotVecMat',
- FunctionDecl(
- name='mju_rotVecMat',
- return_type=ValueType(name='void'),
- parameters=(
- FunctionParameterDecl(
- name='res',
- type=ArrayType(
- inner_type=ValueType(name='mjtNum'),
- extents=(3,),
- ),
- ),
- FunctionParameterDecl(
- name='vec',
- type=ArrayType(
- inner_type=ValueType(name='mjtNum', is_const=True),
- extents=(3,),
- ),
- ),
- FunctionParameterDecl(
- name='mat',
- type=ArrayType(
- inner_type=ValueType(name='mjtNum', is_const=True),
- extents=(9,),
- ),
- ),
- ),
- doc='Deprecated, use mju_mulMatVec3(res, mat, vec).',
- )),
- ('mju_rotVecMatT',
- FunctionDecl(
- name='mju_rotVecMatT',
- return_type=ValueType(name='void'),
- parameters=(
- FunctionParameterDecl(
- name='res',
- type=ArrayType(
- inner_type=ValueType(name='mjtNum'),
- extents=(3,),
- ),
- ),
- FunctionParameterDecl(
- name='vec',
- type=ArrayType(
- inner_type=ValueType(name='mjtNum', is_const=True),
- extents=(3,),
- ),
- ),
- FunctionParameterDecl(
- name='mat',
- type=ArrayType(
- inner_type=ValueType(name='mjtNum', is_const=True),
- extents=(9,),
- ),
- ),
- ),
- doc='Deprecated, use mju_mulMatTVec3(res, mat, vec).',
- )),
('mju_cross',
FunctionDecl(
name='mju_cross',
diff --git a/python/mujoco/functions.cc b/python/mujoco/functions.cc
index 0770611d..6965957d 100644
--- a/python/mujoco/functions.cc
+++ b/python/mujoco/functions.cc
@@ -720,8 +720,6 @@ PYBIND11_MODULE(_functions, pymodule) {
Def(pymodule);
Def(pymodule);
Def(pymodule);
- // skipped: mju_rotVecMat
- // skipped: mju_rotVecMatT
Def(pymodule);
Def(pymodule);
Def(pymodule);
diff --git a/src/engine/engine_util_blas.c b/src/engine/engine_util_blas.c
index af1a0447..c870c6fb 100644
--- a/src/engine/engine_util_blas.c
+++ b/src/engine/engine_util_blas.c
@@ -189,20 +189,6 @@ void mju_mulMatTVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]) {
-// multiply vector by 3D rotation matrix (deprecated)
-void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]) {
- mju_mulMatVec3(res, mat, vec);
-}
-
-
-
-// multiply vector by transposed 3D rotation matrix (deprecated)
-void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]) {
- mju_mulMatTVec3(res, mat, vec);
-}
-
-
-
// multiply 3x3 matrices,
void mju_mulMatMat3(mjtNum res[9], const mjtNum mat1[9], const mjtNum mat2[9]) {
res[0] = mat1[0]*mat2[0] + mat1[1]*mat2[3] + mat1[2]*mat2[6];
diff --git a/src/engine/engine_util_blas.h b/src/engine/engine_util_blas.h
index 77f9a6a7..619a3225 100644
--- a/src/engine/engine_util_blas.h
+++ b/src/engine/engine_util_blas.h
@@ -112,12 +112,6 @@ MJAPI void mju_mulMatVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3
// multiply transposed 3-by-3 matrix by vector
MJAPI void mju_mulMatTVec3(mjtNum res[3], const mjtNum mat[9], const mjtNum vec[3]);
-// multiply vector by 3D rotation matrix (deprecated)
-MJAPI void mju_rotVecMat(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
-
-// multiply vector by transposed 3D rotation matrix (deprecated)
-MJAPI void mju_rotVecMatT(mjtNum res[3], const mjtNum vec[3], const mjtNum mat[9]);
-
// multiply 3x3 matrices
MJAPI void mju_mulMatMat3(mjtNum res[9], const mjtNum mat1[9], const mjtNum mat2[9]);
diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs
index dbc5364b..4eab533b 100644
--- a/unity/Runtime/Bindings/MjBindings.cs
+++ b/unity/Runtime/Bindings/MjBindings.cs
@@ -7141,12 +7141,6 @@ public static unsafe extern void mju_mulMatVec3(double* res, double* mat, double
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void mju_mulMatTVec3(double* res, double* mat, double* vec);
-[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
-public static unsafe extern void mju_rotVecMat(double* res, double* vec, double* mat);
-
-[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
-public static unsafe extern void mju_rotVecMatT(double* res, double* vec, double* mat);
-
[DllImport("mujoco", CallingConvention = CallingConvention.Cdecl)]
public static unsafe extern void mju_cross(double* res, double* a, double* b);
From bebec52869af5d3182c998803dd73c8b7744ee4b Mon Sep 17 00:00:00 2001
From: Alessio Quaglino
Date: Thu, 24 Oct 2024 13:00:38 -0700
Subject: [PATCH 008/426] Add find_all with string input.
PiperOrigin-RevId: 689487165
Change-Id: I48511aec1610b2112f9456e8f410d077c57c7a95
---
python/mujoco/specs.cc | 104 +++++++++++++++++++++++-------------
python/mujoco/specs_test.py | 30 ++++++-----
2 files changed, 82 insertions(+), 52 deletions(-)
diff --git a/python/mujoco/specs.cc b/python/mujoco/specs.cc
index 2359de42..96bb7baf 100644
--- a/python/mujoco/specs.cc
+++ b/python/mujoco/specs.cc
@@ -155,6 +155,48 @@ void DefineArray(py::module& m, const std::string& typestr) {
}, py::keep_alive<0, 1>(), py::return_value_policy::reference_internal);
};
+py::list FindAllImpl(raw::MjsBody& body, mjtObj objtype) {
+ py::list list;
+ raw::MjsElement* el = mjs_firstChild(&body, objtype, true);
+ std::string error = mjs_getError(mjs_getSpec(body.element));
+ if (!el && !error.empty()) {
+ throw pybind11::value_error(error);
+ }
+ while (el) {
+ switch (objtype) {
+ case mjOBJ_BODY:
+ list.append(mjs_asBody(el));
+ break;
+ case mjOBJ_CAMERA:
+ list.append(mjs_asCamera(el));
+ break;
+ case mjOBJ_FRAME:
+ list.append(mjs_asFrame(el));
+ break;
+ case mjOBJ_GEOM:
+ list.append(mjs_asGeom(el));
+ break;
+ case mjOBJ_JOINT:
+ list.append(mjs_asJoint(el));
+ break;
+ case mjOBJ_LIGHT:
+ list.append(mjs_asLight(el));
+ break;
+ case mjOBJ_SITE:
+ list.append(mjs_asSite(el));
+ break;
+ default:
+ // this should never happen
+ throw pybind11::value_error(
+ "body.find_all supports the types: body, frame, geom, site, "
+ "light, camera.");
+ break;
+ }
+ el = mjs_nextChild(&body, el, true);
+ }
+ return list; // list of pointers, so they can be copied
+}
+
PYBIND11_MODULE(_specs, m) {
auto structs_m = py::module::import("mujoco._structs");
py::function mjmodel_from_spec_ptr =
@@ -419,45 +461,31 @@ PYBIND11_MODULE(_specs, m) {
mjsBody.def(
"find_all",
[](raw::MjsBody& self, mjtObj objtype) -> py::list {
- py::list list;
- raw::MjsElement* el = mjs_firstChild(&self, objtype, true);
- std::string error = mjs_getError(mjs_getSpec(self.element));
- if (!el && !error.empty()) {
- throw pybind11::value_error(error);
+ return FindAllImpl(self, objtype);
+ },
+ py::return_value_policy::reference_internal);
+ mjsBody.def(
+ "find_all",
+ [](raw::MjsBody& self, std::string& name) -> py::list {
+ mjtObj objtype = mjOBJ_UNKNOWN;
+ if (name == "body") {
+ objtype = mjOBJ_BODY;
+ } else if (name == "frame") {
+ objtype = mjOBJ_FRAME;
+ } else if (name == "geom") {
+ objtype = mjOBJ_GEOM;
+ } else if (name == "site") {
+ objtype = mjOBJ_SITE;
+ } else if (name == "light") {
+ objtype = mjOBJ_LIGHT;
+ } else if (name == "camera") {
+ objtype = mjOBJ_CAMERA;
+ } else {
+ throw pybind11::value_error(
+ "body.find_all supports the types: body, frame, geom, site, "
+ "light, camera.");
}
- while (el) {
- switch (objtype) {
- case mjOBJ_BODY:
- list.append(mjs_asBody(el));
- break;
- case mjOBJ_CAMERA:
- list.append(mjs_asCamera(el));
- break;
- case mjOBJ_FRAME:
- list.append(mjs_asFrame(el));
- break;
- case mjOBJ_GEOM:
- list.append(mjs_asGeom(el));
- break;
- case mjOBJ_JOINT:
- list.append(mjs_asJoint(el));
- break;
- case mjOBJ_LIGHT:
- list.append(mjs_asLight(el));
- break;
- case mjOBJ_SITE:
- list.append(mjs_asSite(el));
- break;
- default:
- // this should never happen
- throw pybind11::value_error(
- "body.find_all supports the types: body, frame, geom, site, "
- "light, camera.");
- break;
- }
- el = mjs_nextChild(&self, el, true);
- }
- return list;
+ return FindAllImpl(self, objtype);
},
py::return_value_policy::reference_internal);
mjsBody.def(
diff --git a/python/mujoco/specs_test.py b/python/mujoco/specs_test.py
index b5955e5c..450c1f78 100644
--- a/python/mujoco/specs_test.py
+++ b/python/mujoco/specs_test.py
@@ -611,7 +611,6 @@ class SpecsTest(absltest.TestCase):
"""
spec = mujoco.MjSpec.from_string(main_xml)
bodytype = mujoco.mjtObj.mjOBJ_BODY
- sitetype = mujoco.mjtObj.mjOBJ_SITE
self.assertLen(spec.bodies, 5)
self.assertEqual(spec.bodies[1].name, 'body1')
self.assertEqual(spec.bodies[2].name, 'body2')
@@ -620,23 +619,26 @@ class SpecsTest(absltest.TestCase):
self.assertLen(spec.worldbody.find_all(bodytype), 4)
self.assertLen(spec.bodies[1].find_all(bodytype), 2)
self.assertLen(spec.bodies[3].find_all(bodytype), 1)
- self.assertEqual(spec.worldbody.find_all(bodytype)[0].name, 'body1')
- self.assertEqual(spec.worldbody.find_all(bodytype)[1].name, 'body2')
- self.assertEqual(spec.worldbody.find_all(bodytype)[2].name, 'body3')
- self.assertEqual(spec.worldbody.find_all(bodytype)[3].name, 'body4')
- self.assertEqual(spec.bodies[1].find_all(bodytype)[0].name, 'body3')
- self.assertEqual(spec.bodies[1].find_all(bodytype)[1].name, 'body4')
- self.assertEqual(spec.bodies[3].find_all(bodytype)[0].name, 'body4')
- self.assertEmpty(spec.bodies[2].find_all(bodytype))
- self.assertEmpty(spec.bodies[4].find_all(bodytype))
- self.assertEqual(spec.worldbody.find_all(sitetype)[0].name, 'site')
+ self.assertEqual(spec.worldbody.find_all('body')[0].name, 'body1')
+ self.assertEqual(spec.worldbody.find_all('body')[1].name, 'body2')
+ self.assertEqual(spec.worldbody.find_all('body')[2].name, 'body3')
+ self.assertEqual(spec.worldbody.find_all('body')[3].name, 'body4')
+ self.assertEqual(spec.bodies[1].find_all('body')[0].name, 'body3')
+ self.assertEqual(spec.bodies[1].find_all('body')[1].name, 'body4')
+ self.assertEqual(spec.bodies[3].find_all('body')[0].name, 'body4')
+ self.assertEmpty(spec.bodies[2].find_all('body'))
+ self.assertEmpty(spec.bodies[4].find_all('body'))
+ self.assertEqual(spec.worldbody.find_all('site')[0].name, 'site')
with self.assertRaises(ValueError) as cm:
- spec.worldbody.find_all(mujoco.mjtObj.mjOBJ_ACTUATOR)
+ spec.worldbody.find_all('actuator')
self.assertEqual(
str(cm.exception),
- 'Error: Body.NextChild supports the types: body, frame, geom, site,'
- ' light, camera\nElement name \'world\', id 0',
+ 'body.find_all supports the types: body, frame, geom, site,'
+ ' light, camera.',
)
+ body4 = spec.worldbody.find_all('body')[3]
+ body4.name = 'body4_new'
+ self.assertEqual(spec.bodies[4].name, 'body4_new')
def test_iterators(self):
spec = mujoco.MjSpec()
From 2de430a61f4bb2ebd9c12d90e983cc35925fbea4 Mon Sep 17 00:00:00 2001
From: Baruch Tabanpour
Date: Thu, 24 Oct 2024 15:34:07 -0700
Subject: [PATCH 009/426] Use newton by default in benchmark.
PiperOrigin-RevId: 689538944
Change-Id: I99b5588ae5980eee43fb33ac390122ce38000dd3
---
mjx/mujoco/mjx/_src/test_util.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mjx/mujoco/mjx/_src/test_util.py b/mjx/mujoco/mjx/_src/test_util.py
index 349cab16..a5a3af01 100644
--- a/mjx/mujoco/mjx/_src/test_util.py
+++ b/mjx/mujoco/mjx/_src/test_util.py
@@ -53,7 +53,7 @@ def benchmark(
nstep: int = 1000,
batch_size: int = 1024,
unroll_steps: int = 1,
- solver: str = 'cg',
+ solver: str = 'newton',
iterations: int = 1,
ls_iterations: int = 4,
) -> Tuple[float, float, int]:
From f24de91cc9d6724b838bafc7883a0d463d7f76c3 Mon Sep 17 00:00:00 2001
From: Baruch Tabanpour
Date: Thu, 24 Oct 2024 16:07:28 -0700
Subject: [PATCH 010/426] Use eq_active in MJX. Fixes #2173.
PiperOrigin-RevId: 689549368
Change-Id: I14d9817c5ca5dfc2a735ab8bacfb11b75c9feadc
---
doc/changelog.rst | 1 +
mjx/mujoco/mjx/_src/constraint.py | 38 +++++++++++++++++++-------
mjx/mujoco/mjx/_src/constraint_test.py | 14 ++++++++--
3 files changed, 41 insertions(+), 12 deletions(-)
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 8f69bb07..109c54f9 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -15,6 +15,7 @@ MJX
^^^
- Added ``apply_ft``, ``jac``, and ``xfrc_accumulate`` as public functions.
- Added ``TOUCH`` sensor.
+- Added support for ``eq_active``. Fixes :github:issue:`2173`.
Bug fixes
^^^^^^^^^
diff --git a/mjx/mujoco/mjx/_src/constraint.py b/mjx/mujoco/mjx/_src/constraint.py
index 26b684e8..158d9157 100644
--- a/mjx/mujoco/mjx/_src/constraint.py
+++ b/mjx/mujoco/mjx/_src/constraint.py
@@ -107,7 +107,9 @@ def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]:
return None
@jax.vmap
- def rows(is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp):
+ def rows(
+ is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp, active
+ ):
anchor1, anchor2 = data[0:3], data[3:6]
pos1 = d.xmat[body1id] @ anchor1 + d.xpos[body1id]
@@ -128,7 +130,8 @@ def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]:
invweight = m.body_invweight0[body1id, 0] + m.body_invweight0[body2id, 0]
zero = jp.zeros_like(pos)
- return _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
+ efc = _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
+ return jax.tree_util.tree_map(lambda x: x * active, efc)
is_site = m.eq_objtype == ObjType.SITE
body1id = np.copy(m.eq_obj1id)
@@ -147,6 +150,7 @@ def _efc_equality_connect(m: Model, d: Data) -> Optional[_Efc]:
m.eq_data,
m.eq_solref,
m.eq_solimp,
+ d.eq_active,
)
args = jax.tree_util.tree_map(lambda x: x[eq_id], args)
# concatenate to drop row grouping
@@ -161,7 +165,9 @@ def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]:
return None
@jax.vmap
- def rows(is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp):
+ def rows(
+ is_site, obj1id, obj2id, body1id, body2id, data, solref, solimp, active
+ ):
anchor1, anchor2 = data[0:3], data[3:6]
relpose, torquescale = data[6:10], data[10]
@@ -208,7 +214,8 @@ def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]:
invweight = jp.repeat(invweight, 3, axis=0)
zero = jp.zeros_like(pos)
- return _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
+ efc = _row(j, pos, pos_imp, invweight, solref, solimp, zero, zero)
+ return jax.tree_util.tree_map(lambda x: x * active, efc)
is_site = m.eq_objtype == ObjType.SITE
body1id = np.copy(m.eq_obj1id)
@@ -227,6 +234,7 @@ def _efc_equality_weld(m: Model, d: Data) -> Optional[_Efc]:
m.eq_data,
m.eq_solref,
m.eq_solimp,
+ d.eq_active,
)
args = jax.tree_util.tree_map(lambda x: x[eq_id], args)
# concatenate to drop row grouping
@@ -242,7 +250,9 @@ def _efc_equality_joint(m: Model, d: Data) -> Optional[_Efc]:
return None
@jax.vmap
- def rows(obj2id, data, solref, solimp, dofadr1, dofadr2, qposadr1, qposadr2):
+ def rows(
+ obj2id, data, solref, solimp, active, dofadr1, dofadr2, qposadr1, qposadr2
+ ):
pos1, pos2 = d.qpos[qposadr1], d.qpos[qposadr2]
ref1, ref2 = m.qpos0[qposadr1], m.qpos0[qposadr2]
dif = (pos2 - ref2) * (obj2id > -1)
@@ -255,9 +265,11 @@ def _efc_equality_joint(m: Model, d: Data) -> Optional[_Efc]:
invweight += m.dof_invweight0[dofadr2] * (obj2id > -1)
zero = jp.zeros_like(pos)
- return _row(j, pos, pos, invweight, solref, solimp, zero, zero)
+ efc = _row(j, pos, pos, invweight, solref, solimp, zero, zero)
+ return jax.tree_util.tree_map(lambda x: x * active, efc)
args = (m.eq_obj1id, m.eq_obj2id, m.eq_data, m.eq_solref, m.eq_solimp)
+ args += (d.eq_active,)
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]]
@@ -274,7 +286,7 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
if (m.opt.disableflags & DisableBit.EQUALITY) or eq_id.size == 0:
return None
- obj1id, obj2id, data, solref, solimp = jax.tree_util.tree_map(
+ obj1id, obj2id, data, solref, solimp, active = jax.tree_util.tree_map(
lambda x: x[eq_id],
(
m.eq_obj1id,
@@ -282,11 +294,14 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
m.eq_data,
m.eq_solref,
m.eq_solimp,
+ d.eq_active,
),
)
@jax.vmap
- def rows(obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2):
+ def rows(
+ obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2, active
+ ):
dif = pos2 * (obj2id > -1)
dif_power = jp.power(dif, jp.arange(0, 5))
pos = pos1 - jp.dot(data[:5], dif_power)
@@ -294,7 +309,8 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
j = jac1 + jac2 * -deriv
zero = jp.zeros_like(pos)
- return _row(j, pos, pos, invweight, solref, solimp, zero, zero)
+ efc = _row(j, pos, pos, invweight, solref, solimp, zero, zero)
+ return jax.tree_util.tree_map(lambda x: x * active, efc)
inv1, inv2 = m.tendon_invweight0[obj1id], m.tendon_invweight0[obj2id]
jac1, jac2 = d.ten_J[obj1id], d.ten_J[obj2id]
@@ -302,7 +318,9 @@ def _efc_equality_tendon(m: Model, d: Data) -> Optional[_Efc]:
pos2 = d.ten_length[obj2id] - m.tendon_length0[obj2id]
invweight = inv1 + inv2 * (obj2id > -1)
- return rows(obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2)
+ return rows(
+ obj2id, data, solref, solimp, invweight, jac1, jac2, pos1, pos2, active
+ )
def _efc_friction(m: Model, d: Data) -> Optional[_Efc]:
diff --git a/mjx/mujoco/mjx/_src/constraint_test.py b/mjx/mujoco/mjx/_src/constraint_test.py
index ceb43147..76d9bc43 100644
--- a/mjx/mujoco/mjx/_src/constraint_test.py
+++ b/mjx/mujoco/mjx/_src/constraint_test.py
@@ -41,10 +41,17 @@ def _assert_attr_eq(a, b, attr):
class ConstraintTest(parameterized.TestCase):
+ def setUp(self):
+ super().setUp()
+ np.random.seed(42)
+
@parameterized.parameters(
- mujoco.mjtCone.mjCONE_PYRAMIDAL, mujoco.mjtCone.mjCONE_ELLIPTIC
+ {'cone': mujoco.mjtCone.mjCONE_PYRAMIDAL, 'rand_eq_active': False},
+ {'cone': mujoco.mjtCone.mjCONE_ELLIPTIC, 'rand_eq_active': False},
+ {'cone': mujoco.mjtCone.mjCONE_PYRAMIDAL, 'rand_eq_active': True},
+ {'cone': mujoco.mjtCone.mjCONE_ELLIPTIC, 'rand_eq_active': True},
)
- def test_constraints(self, cone):
+ def test_constraints(self, cone, rand_eq_active):
"""Test constraints."""
m = test_util.load_test_file('constraints.xml')
m.opt.cone = cone
@@ -53,6 +60,8 @@ class ConstraintTest(parameterized.TestCase):
# sample a mix of active/inactive constraints at different timesteps
for key in range(3):
mujoco.mj_resetDataKeyframe(m, d, key)
+ if rand_eq_active:
+ d.eq_active[:] = np.random.randint(0, 2, size=m.neq)
mujoco.mj_forward(m, d)
mx = mjx.put_model(m)
dx = mjx.put_data(m, d)
@@ -66,6 +75,7 @@ class ConstraintTest(parameterized.TestCase):
_assert_eq(0, dx.efc_aref[order][d.nefc :], 'efc_aref')
_assert_eq(d.efc_D, dx.efc_D[order][: d.nefc], 'efc_D')
_assert_eq(d.efc_pos, dx.efc_pos[order][: d.nefc], 'efc_pos')
+ _assert_eq(dx.efc_pos[order][d.nefc:], 0, 'efc_pos')
_assert_eq(
d.efc_frictionloss,
dx.efc_frictionloss[order][: d.nefc],
From 455116466b972a208b72d32f33d30ea4299ba148 Mon Sep 17 00:00:00 2001
From: Silvio Traversaro
Date: Mon, 21 Oct 2024 11:25:28 +0200
Subject: [PATCH 011/426] Fix running tests out of a installed mujoco python
package
---
python/mujoco/specs_test.py | 9 +-
python/mujoco/testdata/model.xml | 178 +++++++++++++++++++++++++++++++
2 files changed, 183 insertions(+), 4 deletions(-)
create mode 100644 python/mujoco/testdata/model.xml
diff --git a/python/mujoco/specs_test.py b/python/mujoco/specs_test.py
index 84f10995..96f3d2fd 100644
--- a/python/mujoco/specs_test.py
+++ b/python/mujoco/specs_test.py
@@ -18,6 +18,7 @@ import inspect
import textwrap
from absl.testing import absltest
+from etils import epath
import mujoco
import numpy as np
@@ -433,10 +434,11 @@ class SpecsTest(absltest.TestCase):
)
def test_load_xml(self):
- filename = '../../test/testdata/model.xml'
state_type = mujoco.mjtState.mjSTATE_INTEGRATION
# Load from file.
+ file_path = epath.resource_path("mujoco") / "testdata" / "model.xml"
+ filename = file_path.as_posix()
spec1 = mujoco.MjSpec.from_file(filename)
model1 = spec1.compile()
data1 = mujoco.MjData(model1)
@@ -692,9 +694,8 @@ class SpecsTest(absltest.TestCase):
mujoco.mjtGeom.mjGEOM_BOX)
def test_delete(self):
- filename = '../../test/testdata/model.xml'
-
- spec = mujoco.MjSpec.from_file(filename)
+ file_path = epath.resource_path("mujoco") / "testdata" / "model.xml"
+ spec = mujoco.MjSpec.from_file(file_path.as_posix())
model = spec.compile()
self.assertIsNotNone(model)
diff --git a/python/mujoco/testdata/model.xml b/python/mujoco/testdata/model.xml
new file mode 100644
index 00000000..98274616
--- /dev/null
+++ b/python/mujoco/testdata/model.xml
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From 1201db8050b4800d436cce545ffd4dd723d7602b Mon Sep 17 00:00:00 2001
From: Silvio
Date: Fri, 25 Oct 2024 09:33:17 +0200
Subject: [PATCH 012/426] Do not test python bindings in python/dist directory
This will permit to early catch tests that rely on files not
installed in the wheel, i.e. catch if pytest --pyargs mujoco fails in a vanilla environment.
---
.github/workflows/build.yml | 1 -
1 file changed, 1 deletion(-)
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 1f6364d9..b22215b6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -239,7 +239,6 @@ jobs:
- name: Test Python bindings
if: ${{ runner.os != 'Windows' }}
shell: bash
- working-directory: python/dist
env:
MUJOCO_GL: disable
run: >
From 078c7bb42eda6a9678782ca67f7d4a8f5156e4c6 Mon Sep 17 00:00:00 2001
From: Baruch Tabanpour
Date: Fri, 25 Oct 2024 15:28:29 -0700
Subject: [PATCH 013/426] internal change
PiperOrigin-RevId: 689931872
Change-Id: Icc7b261b6d98e181a6bf0845f44a050af2833db2
---
mjx/mujoco/mjx/_src/collision_driver.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mjx/mujoco/mjx/_src/collision_driver.py b/mjx/mujoco/mjx/_src/collision_driver.py
index 796ef3fa..129bcf2a 100644
--- a/mjx/mujoco/mjx/_src/collision_driver.py
+++ b/mjx/mujoco/mjx/_src/collision_driver.py
@@ -377,11 +377,11 @@ def collision(m: Model, d: Data) -> Data:
if d.ncon == 0:
return d
- groups = _contact_groups(m, d)
max_geom_pairs = _numeric(m, 'max_geom_pairs')
max_contact_points = _numeric(m, 'max_contact_points')
# run collision functions on groups
+ groups = _contact_groups(m, d)
for key, contact in groups.items():
# determine which contacts we'll use for collision testing by running a
# broad phase cull if requested
From 7cf457286d045870dd46a0d23317a3be884abb5a Mon Sep 17 00:00:00 2001
From: Philipp Schmutz <2059887+pschmutz@users.noreply.github.com>
Date: Sat, 26 Oct 2024 16:22:47 +0200
Subject: [PATCH 014/426] Print error instead of failing silently when loading
plugins
Previously the return value of dlopen was ignored, meaning a failed load of a shared library would stay unnoticed
---
src/engine/engine_plugin.cc | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/src/engine/engine_plugin.cc b/src/engine/engine_plugin.cc
index 93ea4233..20a64bf1 100644
--- a/src/engine/engine_plugin.cc
+++ b/src/engine/engine_plugin.cc
@@ -387,7 +387,15 @@ void mj_loadPluginLibrary(const char* path) {
#if defined(_WIN32) || defined(__CYGWIN__)
LoadLibraryA(path);
#else
- dlopen(path, RTLD_NOW | RTLD_LOCAL);
+ void* handle = dlopen(path, RTLD_NOW | RTLD_LOCAL);
+ if (!handle) {
+ const char* error = dlerror();
+ if (error) {
+ mju_error("Error loading plugin library '%s': %s\n", path, error);
+ } else {
+ mju_error("Unknown error loading plugin library '%s'\n", path);
+ }
+ }
#endif
}
From 61cb552f8ad0ea377c831ac53dc559576f0e6908 Mon Sep 17 00:00:00 2001
From: Yuval Tassa
Date: Sat, 26 Oct 2024 07:40:49 -0700
Subject: [PATCH 015/426] Refactor PBR texture layers from separate
sub-elements to a single layer sub-element.
Add new element to PyMJCF schema.
PiperOrigin-RevId: 690115286
Change-Id: I7bb3f184cb321ca96037b2ca0ee160efef37469d
---
doc/APIreference/APItypes.rst | 2 +-
doc/XMLreference.rst | 166 +++++++++--------------------
doc/XMLschema.rst | 62 +----------
doc/changelog.rst | 5 +-
src/xml/xml_native_reader.cc | 29 ++---
src/xml/xml_native_reader.h | 2 +-
test/xml/xml_native_reader_test.cc | 14 +--
7 files changed, 80 insertions(+), 200 deletions(-)
diff --git a/doc/APIreference/APItypes.rst b/doc/APIreference/APItypes.rst
index fba959b2..3f11ee5e 100644
--- a/doc/APIreference/APItypes.rst
+++ b/doc/APIreference/APItypes.rst
@@ -1143,7 +1143,7 @@ behavior.
.. _mjsCompiler:
mjsCompiler
-~~~~~~~~~~
+~~~~~~~~~~~
Compiler options.
diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst
index 8c3e72de..f2b030db 100644
--- a/doc/XMLreference.rst
+++ b/doc/XMLreference.rst
@@ -1247,19 +1247,16 @@ The full list of processing steps applied by the compiler to each mesh is as fol
:at:`inertia`: :at-val:`[convex, exact, legacy], "legacy"`
This attribute controls how the mesh is used when mass and inertia are
- :ref:`inferred from geometry<_compiler-inertiafromgeom>`. The current default value :at-val:`legacy` will be changed
+ :ref:`inferred from geometry`. The current default value :at-val:`legacy` will be changed
to :at-val:`convex` in a future release.
- :at-val:`convex`
- Use the mesh's convex hull to compute volume and inertia.
+ :at-val:`convex`: Use the mesh's convex hull to compute volume and inertia.
- :at-val:`exact`
- Use an exact algorithm to compute volume and inertia. This algorithm requires a well-oriented, watertight mesh and
- will error otherwise.
+ :at-val:`exact`: Use an exact algorithm to compute volume and inertia. This algorithm requires a well-oriented,
+ watertight mesh and will error otherwise.
- :at-val:`legacy`
- Use the legacy algorithm, which is similar to :at-val:`convex`, but leads to volume overcounting for non-convex
- meshes.
+ :at-val:`legacy`: Use the legacy algorithm, which is similar to :at-val:`convex`, but leads to volume overcounting
+ for non-convex meshes.
.. _asset-mesh-smoothnormal:
@@ -1710,9 +1707,9 @@ properties are grouped together.
loaded explicitly via the :ref:`texture ` element and then referenced here. The texture referenced
here is used for specifying the RGB values. For advanced rendering (e.g., Physics-Based Rendering), more texture
types need to be specified (e.g., roughness, metallic). In this case, this texture attribute should be omitted, and
- the texture types should be specified explicitly via the specific role child elements, e.g.,
- :ref:`texture `. Note however that the built-in renderer does not support PBR properties, so these
- advanced rendering features are only available when using an external renderer.
+ the texture types should be specified using :ref:`layer ` child elements. Note however that the
+ built-in renderer does not support PBR properties, so these advanced rendering features are only available when using
+ an external renderer.
.. _asset-material-texrepeat:
@@ -1784,116 +1781,59 @@ properties are grouped together.
model element which defines its own local rgba attribute, the local definition has precedence. Note that this "local"
definition could in fact come from a defaults class. The remaining material properties always apply.
-.. _material-rgb:
+.. _material-layer:
-:el-prefix:`material/` |-| **rgb** (?)
-''''''''''''''''''''''''''''''''''''''
+:el-prefix:`material/` |-| **layer** (?)
+''''''''''''''''''''''''''''''''''''''''
-This element references a texture asset used to specify base color / albedo values.
+If multiple textures are needed to specify the appearance of a material, the :ref:`texture `
+attribute cannot be used, and :el:`layer` child elements must be used instead. Specifying both the :at:`texture`
+attribute and :el:`layer` child elements is an error.
-.. _material-rgb-texture:
+.. _material-layer-texture:
:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly 3 channels.
+ Name of the texture, like the :ref:`texture ` attribute.
-.. _material-normal:
+.. _material-layer-role:
-:el-prefix:`material/` |-| **normal** (?)
-'''''''''''''''''''''''''''''''''''''''''
+:at:`role`: :at-val:`string, required`
+ Role of the texture. The valid values, expected number of channels, and the role semantics are:
-This element references a texture asset used to specify the bump map (surface normals).
-
-.. _material-normal-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly 3 channels.
-
-.. _material-occlusion:
-
-:el-prefix:`material/` |-| **occlusion** (?)
-''''''''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify ambient occlusion.
-
-.. _material-occlusion-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly one channel.
-
-.. _material-roughness:
-
-:el-prefix:`material/` |-| **roughness** (?)
-''''''''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify the roughness map.
-
-.. _material-roughness-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly one channel.
-
-.. _material-metallic:
-
-:el-prefix:`material/` |-| **metallic** (?)
-'''''''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify the metallic map.
-
-.. _material-metallic-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly one channel.
-
-.. _material-opacity:
-
-:el-prefix:`material/` |-| **opacity** (?)
-''''''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify the opacity map (alpha channel, transparency).
-
-.. _material-opacity-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly one channel.
-
-.. _material-emissive:
-
-:el-prefix:`material/` |-| **emissive** (?)
-'''''''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify light emission.
-
-.. _material-emissive-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly 4 channels.
-
-.. _material-orm:
-
-:el-prefix:`material/` |-| **orm** (?)
-''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify a packed ORM map, where occlusion, roughness, and metallic
-are joined into the corresponding RGB values of a single texture.
-
-.. _material-orm-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly 3 channels.
-
-.. _material-rgba:
-
-:el-prefix:`material/` |-| **rgba** (?)
-'''''''''''''''''''''''''''''''''''''''
-
-This element references a texture asset used to specify a packed map where albedo and opacity are joined into the same
-4-channel texture.
-
-.. _material-rgba-texture:
-
-:at:`texture`: :at-val:`string, required`
- Name of the texture, expected to have exactly 4 channels.
+ .. list-table::
+ :widths: 1 1 8
+ :header-rows: 1
+ * - value
+ - channels
+ - description
+ * - :at:`rgb`
+ - 3
+ - base color / albedo [red, green, blue]
+ * - :at:`normal`
+ - 3
+ - bump map (surface normals)
+ * - :at:`occlusion`
+ - 1
+ - ambient occlusion
+ * - :at:`roughness`
+ - 1
+ - roughness
+ * - :at:`metallic`
+ - 1
+ - metallicity
+ * - :at:`opacity`
+ - 1
+ - opacity (alpha channel)
+ * - :at:`emissive`
+ - 4
+ - RGB light emmision intensity, exposure weight in 4th channel
+ * - :at:`orm`
+ - 3
+ - packed 3 channel [occlusion, roughness, metallic]
+ * - :at:`rgba`
+ - 4
+ - packed 4 channel [red, green, blue, alpha]
.. _asset-model:
diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst
index 4b45b68b..72df2be3 100644
--- a/doc/XMLschema.rst
+++ b/doc/XMLschema.rst
@@ -178,66 +178,10 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`rgb | ? | :class: mjcf-attributes |
-| ` | | |
+| :ref:`layer | \* | :class: mjcf-attributes |
+| ` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`occlusion | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`roughness | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`metallic | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`normal | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`opacity | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`emissive | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`rgba | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-| |_2| material |br| |_2| |L| | | .. table:: |
-| :ref:`orm | ? | :class: mjcf-attributes |
-| ` | | |
-| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`texture` | | | | |
+| | | | :ref:`texture` | :ref:`role` | | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_| asset |br| |_| |L| | | .. table:: |
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 109c54f9..920cff46 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -10,6 +10,9 @@ General
- The global compiler flag ``exactmeshinertia`` has been removed and replaced with the mesh-specific
:ref:`inertia` attribute.
- Removed the deprecated ``mju_rotVecMat`` and ``mju_rotVecMatT`` functions.
+- Sorting now uses a faster, native sort function (fixes :github:issue:`1638`).
+- The PBR texture layers introduced in 3.2.1 were refactored from seperate sub-elements to a single
+ :ref:`layer` sub-element.
MJX
^^^
@@ -169,7 +172,7 @@ General
2. Added a new :ref:`autoreset` flag to disable automatic reset when NaNs or infinities are
detected.
3. Added sub-elements to the MJCF :ref:`material` element, to allow specification of multiple textures
- for rendering (e.g., :ref:`occlusion-roughness-metallic`). Note that the MuJoCo renderer doesn't
+ for rendering (e.g., ``occlusion, roughness, metallic``). Note that the MuJoCo renderer doesn't
support these new features, and they are made available for use with external renderers.
4. Sorting (``mjQUICKSORT``) now calls ``std::sort`` when building with C++ (:github:issue:`1638`).
diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc
index 854459cb..6fc675c5 100644
--- a/src/xml/xml_native_reader.cc
+++ b/src/xml/xml_native_reader.cc
@@ -247,15 +247,7 @@ const char* MJCF[nMJCF][mjXATTRNUM] = {
{"material", "*", "12", "name", "class", "texture", "texrepeat", "texuniform",
"emission", "specular", "shininess", "reflectance", "metallic", "roughness", "rgba"},
{"<"},
- {"rgb", "?", "1", "texture"},
- {"occlusion", "?", "1", "texture"},
- {"roughness", "?", "1", "texture"},
- {"metallic", "?", "1", "texture"},
- {"normal", "?", "1", "texture"},
- {"opacity", "?", "1", "texture"},
- {"emissive", "?", "1", "texture"},
- {"rgba", "?", "1", "texture"},
- {"orm", "?", "1", "texture"},
+ {"layer", "*", "2", "texture", "role"},
{">"},
{"model", "*", "3", "name", "file", "content_type"},
{">"},
@@ -1590,17 +1582,18 @@ void mjXReader::OneMaterial(XMLElement* elem, mjsMaterial* material) {
tex_attributes_found = true;
}
- XMLElement* tex_elem = FirstChildElement(elem);
- while (tex_elem) {
+ XMLElement* layer = FirstChildElement(elem);
+ while (layer) {
if (tex_attributes_found) {
- throw mjXError(tex_elem, "A material with a texture attribute cannot have texture sub-elements");
+ throw mjXError(layer, "A material with a texture attribute cannot have layer sub-elements");
}
- // texture sub-element
- int role = FindKey(texrole_map, texrole_sz, tex_elem->Name());
- string texmat;
- ReadAttrTxt(tex_elem, "texture", texmat, true);
- mjs_setInStringVec(material->textures, role, texmat.c_str());
- tex_elem = NextSiblingElement(tex_elem);
+
+ // layer sub-element
+ ReadAttrTxt(layer, "role", text, true);
+ int role = FindKey(texrole_map, texrole_sz, text);
+ ReadAttrTxt(layer, "texture", text, true);
+ mjs_setInStringVec(material->textures, role, text.c_str());
+ layer = NextSiblingElement(layer);
}
if (MapValue(elem, "texuniform", &n, bool_map, 2)) {
diff --git a/src/xml/xml_native_reader.h b/src/xml/xml_native_reader.h
index c9f456bf..62488b97 100644
--- a/src/xml/xml_native_reader.h
+++ b/src/xml/xml_native_reader.h
@@ -101,7 +101,7 @@ class mjXReader : public mjXBase {
};
// MJCF schema
-#define nMJCF 245
+#define nMJCF 237
extern const char* MJCF[nMJCF][mjXATTRNUM];
#endif // MUJOCO_SRC_XML_XML_NATIVE_READER_H_
diff --git a/test/xml/xml_native_reader_test.cc b/test/xml/xml_native_reader_test.cc
index 8f902565..4eed4735 100644
--- a/test/xml/xml_native_reader_test.cc
+++ b/test/xml/xml_native_reader_test.cc
@@ -805,10 +805,10 @@ TEST_F(XMLReaderTest, MaterialTextureTest) {
-
-
-
-
+
+
+
+
@@ -873,8 +873,8 @@ TEST_F(XMLReaderTest, MaterialTextureFailTest) {
-
-
+
+
@@ -887,7 +887,7 @@ TEST_F(XMLReaderTest, MaterialTextureFailTest) {
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
EXPECT_THAT(m, IsNull());
EXPECT_THAT(error.data(), HasSubstr("A material with a texture attribute "
- "cannot have texture sub-elements"));
+ "cannot have layer sub-elements"));
}
TEST_F(XMLReaderTest, LargeTextureTest) {
From 7dc8aef807792ff214d230b5fd6e4bbe1e914b6c Mon Sep 17 00:00:00 2001
From: Yuval Tassa
Date: Sun, 27 Oct 2024 04:30:12 -0700
Subject: [PATCH 016/426] Remove the `convexhull` compiler option.
PiperOrigin-RevId: 690307675
Change-Id: I62d8213579ad07296ca96de004d39ae012174aa4
---
doc/XMLreference.rst | 14 +--
doc/XMLschema.rst | 4 +-
doc/changelog.rst | 1 +
doc/includes/references.h | 1 -
include/mujoco/mjspec.h | 1 -
introspect/structs.py | 5 -
src/user/user_init.c | 1 -
src/user/user_mesh.cc | 2 +-
src/xml/xml_base.h | 1 -
src/xml/xml_native_reader.cc | 7 +-
src/xml/xml_native_writer.cc | 3 -
.../testdata/collision_driver/midphase.xml | 2 +-
test/user/user_mesh_test.cc | 102 +++++++++---------
unity/Runtime/Bindings/MjBindings.cs | 1 -
14 files changed, 58 insertions(+), 87 deletions(-)
diff --git a/doc/XMLreference.rst b/doc/XMLreference.rst
index f2b030db..523b0d43 100644
--- a/doc/XMLreference.rst
+++ b/doc/XMLreference.rst
@@ -772,23 +772,11 @@ has any effect. The settings here are global and apply to the entire model.
models compiled with this flag, it is important to remember that collision geoms are often placed in a
:ref:`group` which is invisible by default.
-.. _compiler-convexhull:
-
-:at:`convexhull`: :at-val:`[false, true], "true"`
- If this attribute is "true", the compiler will automatically generate a convex hull for every mesh that is used in at
- least one non-visual geom (in the sense of the discardvisual attribute above). This is done to speed up collision
- detection; recall :ref:`Collision` section in the Computation chapter. Even if the mesh is already convex, the hull
- contains edge information that is not present in the mesh file, so it needs to be constructed. The only reason to
- disable this feature is to speed up re-loading of a model with large meshes during model editing (since the convex
- hull computation is the slowest operation performed by the compiler). However once model design is finished, this
- feature should be enabled, because the availability of convex hulls substantially speeds up collision detection with
- large meshes.
-
.. _compiler-usethread:
:at:`usethread`: :at-val:`[false, true], "true"`
If this attribute is "true", the model compiler will run in multi-threaded mode. Currently multi-threading is used
- for computing the length ranges of actuators and for loading meshes.
+ for computing the length ranges of actuators and for parallel loading of meshes.
.. _compiler-fusestatic:
diff --git a/doc/XMLschema.rst b/doc/XMLschema.rst
index 72df2be3..caf07b20 100644
--- a/doc/XMLschema.rst
+++ b/doc/XMLschema.rst
@@ -52,9 +52,9 @@
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
| | | | :ref:`fitaabb` | :ref:`eulerseq` | :ref:`meshdir` | :ref:`texturedir` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`discardvisual` | :ref:`convexhull` | :ref:`usethread` | :ref:`fusestatic` | |
+| | | | :ref:`discardvisual` | :ref:`usethread` | :ref:`fusestatic` | :ref:`inertiafromgeom` | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
-| | | | :ref:`inertiafromgeom` | :ref:`inertiagrouprange` | :ref:`assetdir` | :ref:`alignfree` | |
+| | | | :ref:`inertiagrouprange` | :ref:`assetdir` | :ref:`alignfree` | | |
| | | +-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+-----------------------------------------------------------------+ |
+------------------------------------+----+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| |_| compiler |br| |_| |L| | | .. table:: |
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 920cff46..6aa3b745 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -9,6 +9,7 @@ General
^^^^^^^
- The global compiler flag ``exactmeshinertia`` has been removed and replaced with the mesh-specific
:ref:`inertia` attribute.
+- The not-useful ``convexhull`` compiler option (to disable computation of mesh convex hulls) has been removed.
- Removed the deprecated ``mju_rotVecMat`` and ``mju_rotVecMatT`` functions.
- Sorting now uses a faster, native sort function (fixes :github:issue:`1638`).
- The PBR texture layers introduced in 3.2.1 were refactored from seperate sub-elements to a single
diff --git a/doc/includes/references.h b/doc/includes/references.h
index e19503d4..e8b6f6e7 100644
--- a/doc/includes/references.h
+++ b/doc/includes/references.h
@@ -1693,7 +1693,6 @@ typedef struct mjsCompiler_ { // compiler options
mjtByte degree; // angles in radians or degrees
char eulerseq[3]; // sequence for euler rotations
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)
diff --git a/include/mujoco/mjspec.h b/include/mujoco/mjspec.h
index 58feb8f8..2dc3177b 100644
--- a/include/mujoco/mjspec.h
+++ b/include/mujoco/mjspec.h
@@ -131,7 +131,6 @@ typedef struct mjsCompiler_ { // compiler options
mjtByte degree; // angles in radians or degrees
char eulerseq[3]; // sequence for euler rotations
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)
diff --git a/introspect/structs.py b/introspect/structs.py
index 9f8c8b4c..0776273e 100644
--- a/introspect/structs.py
+++ b/introspect/structs.py
@@ -8853,11 +8853,6 @@ STRUCTS: Mapping[str, StructDecl] = dict([
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'),
diff --git a/src/user/user_init.c b/src/user/user_init.c
index b5d15413..08fceb59 100644
--- a/src/user/user_init.c
+++ b/src/user/user_init.c
@@ -39,7 +39,6 @@ void mjs_defaultSpec(mjSpec* spec) {
spec->compiler.eulerseq[0] = 'x';
spec->compiler.eulerseq[1] = 'y';
spec->compiler.eulerseq[2] = 'z';
- spec->compiler.convexhull = 1;
spec->compiler.usethread = 1;
spec->compiler.inertiafromgeom = mjINERTIAFROMGEOM_AUTO;
spec->compiler.inertiagrouprange[1] = mjNGROUP-1;
diff --git a/src/user/user_mesh.cc b/src/user/user_mesh.cc
index d1e20f46..695de30f 100644
--- a/src/user/user_mesh.cc
+++ b/src/user/user_mesh.cc
@@ -563,7 +563,7 @@ void mjCMesh::Compile(const mjVFS* vfs) {
}
// make graph describing convex hull
- if ((model->compiler.convexhull && needhull_) || face_.empty()) {
+ if (needhull_ || face_.empty()) {
MakeGraph();
}
diff --git a/src/xml/xml_base.h b/src/xml/xml_base.h
index a022831c..bf2c92d6 100644
--- a/src/xml/xml_base.h
+++ b/src/xml/xml_base.h
@@ -42,7 +42,6 @@ extern const int gain_sz;
extern const int bias_sz;
extern const int stage_sz;
extern const int datatype_sz;
-extern const mjMap coordinate_map[];
extern const mjMap angle_map[];
extern const mjMap enable_map[];
extern const mjMap bool_map[];
diff --git a/src/xml/xml_native_reader.cc b/src/xml/xml_native_reader.cc
index 6fc675c5..6a70d13e 100644
--- a/src/xml/xml_native_reader.cc
+++ b/src/xml/xml_native_reader.cc
@@ -97,9 +97,9 @@ static void UpdateString(string& psuffix, int count, int i) {
const char* MJCF[nMJCF][mjXATTRNUM] = {
{"mujoco", "!", "1", "model"},
{"<"},
- {"compiler", "*", "20", "autolimits", "boundmass", "boundinertia", "settotalmass",
+ {"compiler", "*", "19", "autolimits", "boundmass", "boundinertia", "settotalmass",
"balanceinertia", "strippath", "coordinate", "angle", "fitaabb", "eulerseq",
- "meshdir", "texturedir", "discardvisual", "convexhull", "usethread",
+ "meshdir", "texturedir", "discardvisual", "usethread",
"fusestatic", "inertiafromgeom", "inertiagrouprange", "assetdir", "alignfree"},
{"<"},
{"lengthrange", "?", "10", "mode", "useexisting", "uselimit",
@@ -1008,9 +1008,6 @@ void mjXReader::Compiler(XMLElement* section, mjSpec* spec) {
if (MapValue(section, "discardvisual", &n, bool_map, 2)) {
spec->compiler.discardvisual = (n==1);
}
- if (MapValue(section, "convexhull", &n, bool_map, 2)) {
- spec->compiler.convexhull = (n==1);
- }
if (MapValue(section, "usethread", &n, bool_map, 2)) {
spec->compiler.usethread = (n==1);
}
diff --git a/src/xml/xml_native_writer.cc b/src/xml/xml_native_writer.cc
index 907920a6..e9a47920 100644
--- a/src/xml/xml_native_writer.cc
+++ b/src/xml/xml_native_writer.cc
@@ -920,9 +920,6 @@ void mjXWriter::Compiler(XMLElement* root) {
XMLElement* section = InsertEnd(root, "compiler");
// settings
- if (!model->compiler.convexhull) {
- WriteAttrTxt(section, "convexhull", FindValue(bool_map, 2, model->compiler.convexhull));
- }
WriteAttrTxt(section, "angle", "radian");
if (!model->get_meshdir().empty()) {
WriteAttrTxt(section, "meshdir", model->get_meshdir());
diff --git a/test/engine/testdata/collision_driver/midphase.xml b/test/engine/testdata/collision_driver/midphase.xml
index 3939de92..49bc5dbf 100644
--- a/test/engine/testdata/collision_driver/midphase.xml
+++ b/test/engine/testdata/collision_driver/midphase.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/test/user/user_mesh_test.cc b/test/user/user_mesh_test.cc
index 4bbd3057..0a57109c 100644
--- a/test/user/user_mesh_test.cc
+++ b/test/user/user_mesh_test.cc
@@ -75,7 +75,6 @@ using ::testing::ElementsAre;
using ::testing::HasSubstr;
using ::testing::IsNull;
using ::testing::NotNull;
-using ::testing::StartsWith;
// ------------- test invalid filenames ----------------------------------------
@@ -337,8 +336,7 @@ TEST_F(MjCMeshTest, DeduplicateSTLVertices) {
TEST_F(MjCMeshTest, LoadMSH) {
const std::string xml_path = GetTestDataFilePath(kMeshPath);
char error[1024];
- size_t error_sz = 1024;
- mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, error_sz);
+ mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, sizeof(error));
ASSERT_THAT(model, NotNull()) << error;
ASSERT_EQ(model->nmeshvert, 36);
mj_deleteModel(model);
@@ -418,8 +416,9 @@ TEST_F(MjCMeshTest, TinyMeshLoads) {
)";
- mjModel* model = LoadModelFromString(xml);
- ASSERT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
mj_deleteModel(model);
}
@@ -458,8 +457,9 @@ TEST_F(MjCMeshTest, FaceNormalAutogenerated) {
)";
- mjModel* model = LoadModelFromString(xml);
- ASSERT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
mj_deleteModel(model);
}
@@ -480,9 +480,9 @@ TEST_F(MjCMeshTest, SmallInertiaLoads) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- ASSERT_THAT(model, NotNull()) << error.data();
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
mj_deleteModel(model);
}
@@ -510,18 +510,18 @@ TEST_F(MjCMeshTest, TinyInertiaFails) {
TEST_F(MjCMeshTest, FlippedFaceAllowedLegacyInertia) {
const std::string xml_path = GetTestDataFilePath(kMalformedFaceOBJPath);
- std::array error;
- mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
- EXPECT_THAT(model, testing::NotNull()) << error.data();
+ char error[1024];
+ mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
EXPECT_THAT(model->nmeshface, 4);
mj_deleteModel(model);
}
TEST_F(MjCMeshTest, MissingFaceAllowedConvexInertia) {
const std::string xml_path = GetTestDataFilePath(kCompareInertiaPath);
- std::array error;
- mjModel* model = mj_loadXML(xml_path.c_str(), 0, error.data(), error.size());
- EXPECT_THAT(model, testing::NotNull()) << error.data();
+ char error[1024];
+ mjModel* model = mj_loadXML(xml_path.c_str(), 0, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
EXPECT_THAT(model->nmeshface, 10);
EXPECT_THAT(model->body_inertia[3], model->body_inertia[9]);
EXPECT_THAT(model->body_inertia[4], model->body_inertia[10]);
@@ -583,9 +583,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedWorld) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
CheckTetrahedronWasRescaled(model);
mj_deleteModel(model);
}
@@ -605,9 +605,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedNoMass) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull()) << error.data();
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
CheckTetrahedronWasRescaled(model);
mj_deleteModel(model);
}
@@ -628,9 +628,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedInertial) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
CheckTetrahedronWasRescaled(model);
mj_deleteModel(model);
}
@@ -650,9 +650,9 @@ TEST_F(MjCMeshTest, FlippedFaceAllowedNegligibleArea) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
CheckTetrahedronWasRescaled(model);
mj_deleteModel(model);
}
@@ -706,9 +706,9 @@ TEST_F(MjCMeshTest, AreaTooSmallAllowedWorld) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
mj_deleteModel(model);
}
@@ -736,10 +736,9 @@ TEST_F(MjCMeshTest, VolumeTooSmall) {
TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
static constexpr char xml[] = R"(
-
@@ -749,9 +748,9 @@ TEST_F(MjCMeshTest, VolumeSmallAllowedShell) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- ASSERT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
EXPECT_LE(mju_abs(model->geom_size[0]), 1);
EXPECT_LE(mju_abs(model->geom_size[1]), 1);
EXPECT_LE(mju_abs(model->geom_size[2]), 1);
@@ -815,9 +814,9 @@ TEST_F(MjCMeshTest, VolumeTooSmallAllowedWorld) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
mj_deleteModel(model);
}
@@ -895,8 +894,9 @@ TEST_F(MjCMeshTest, MeshPosQuat) {
)";
- mjModel* model = LoadModelFromString(xml);
- ASSERT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
// Loading the mesh results in an offset of the geom's pos and quat due to the
// fact that the geom's center is not the volumetric center of the mesh. To
// recover the geom's originally specified pose, the offset used is stored in
@@ -953,8 +953,9 @@ TEST_F(MjCMeshTest, MeshScale) {
)";
- mjModel* model = LoadModelFromString(xml);
- ASSERT_THAT(model, NotNull());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
EXPECT_THAT(AsVector(model->mesh_scale + 0, 3), ElementsAre(1, 1, 1));
EXPECT_THAT(AsVector(model->mesh_scale + 3, 3), ElementsAre(0.9, 1, -1));
@@ -972,9 +973,9 @@ TEST_F(MjCMeshTest, CreateFaceTexCoord) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
- EXPECT_THAT(model, NotNull()) << error.data();
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
+ ASSERT_THAT(model, NotNull()) << error;
mj_deleteModel(model);
}
@@ -1057,13 +1058,10 @@ TEST_F(MjCMeshTest, InvalidIndexInFace) {
)";
- std::array error;
- mjModel* model = LoadModelFromString(xml, error.data(), error.size());
+ char error[1024];
+ mjModel* model = LoadModelFromString(xml, error, sizeof(error));
ASSERT_THAT(model, IsNull());
- EXPECT_THAT(
- error.data(),
- HasSubstr(
- "in face 0, vertex index 6 does not exist"));
+ EXPECT_THAT(error, HasSubstr("in face 0, vertex index 6 does not exist"));
mj_deleteModel(model);
}
diff --git a/unity/Runtime/Bindings/MjBindings.cs b/unity/Runtime/Bindings/MjBindings.cs
index 4eab533b..f456e3f6 100644
--- a/unity/Runtime/Bindings/MjBindings.cs
+++ b/unity/Runtime/Bindings/MjBindings.cs
@@ -5727,7 +5727,6 @@ public unsafe struct mjsCompiler_ {
public byte degree;
public fixed sbyte eulerseq[3];
public byte discardvisual;
- public byte convexhull;
public byte usethread;
public byte fusestatic;
public int inertiafromgeom;
From 1c424644dd68875dba95e973e171b358c4f0f5a4 Mon Sep 17 00:00:00 2001
From: Taylor Howell
Date: Mon, 28 Oct 2024 03:38:52 -0700
Subject: [PATCH 017/426] Fix MJX touch sensor.
PiperOrigin-RevId: 690544118
Change-Id: Ic909c6f6ce0e31237cbe317fbbfbb1fd267bd462
---
mjx/mujoco/mjx/_src/sensor.py | 21 ++++++++---------
mjx/mujoco/mjx/_src/sensor_test.py | 3 ---
mjx/mujoco/mjx/test_data/sensor/sensor.xml | 27 ++++++++++++++--------
3 files changed, 28 insertions(+), 23 deletions(-)
diff --git a/mjx/mujoco/mjx/_src/sensor.py b/mjx/mujoco/mjx/_src/sensor.py
index e514bdd2..5d040052 100644
--- a/mjx/mujoco/mjx/_src/sensor.py
+++ b/mjx/mujoco/mjx/_src/sensor.py
@@ -461,7 +461,7 @@ def sensor_acc(m: Model, d: Data) -> Data:
force, condim_id = support.contact_force_dim(m, d, dim)
forces.append(force)
condim_ids.append(condim_id)
- forces = jp.concatenate(forces)[jp.concatenate(condim_ids)]
+ forces = jp.concatenate(forces)[np.argsort(np.concatenate(condim_ids))]
# get bodies of contact geoms
conbody = jp.array(m.geom_bodyid)[d.contact.geom]
@@ -483,14 +483,14 @@ def sensor_acc(m: Model, d: Data) -> Data:
conray = jp.where(conbody1[..., None], -conray, conray)
# compute distance, mapping over sites and contacts
- def _distance(
- site_size, site_xpos, site_xmat, site_type, contact_pos, conray
- ):
- return jax.vmap(
- lambda site_size, site_xpos, site_xmat, conray: jax.vmap(
- lambda pnt, vec: ray.ray_geom(site_size, pnt, vec, site_type)
- )((contact_pos - site_xpos) @ site_xmat, conray @ site_xmat)
- )(site_size, site_xpos, site_xmat, conray)
+ def _distance(site_size, site_xpos, site_xmat, site_type, pos, conray):
+ def dist(size, xpos, xmat, conray):
+ pnt = (pos - xpos) @ xmat
+ vec = conray @ xmat
+ ray_geom_ = lambda pnt, vec: ray.ray_geom(size, pnt, vec, site_type)
+ return jax.vmap(ray_geom_)(pnt, vec)
+
+ return jax.vmap(dist)(site_size, site_xpos, site_xmat, conray)
dist = []
dist_id = []
@@ -506,8 +506,7 @@ def sensor_acc(m: Model, d: Data) -> Data:
)
dist.append(jp.where(jp.isinf(dist_site), 0, dist_site))
dist_id.append(dist_id_site)
-
- dist = jp.vstack(dist)[np.concatenate(dist_id)]
+ dist = jp.vstack(dist)[np.argsort(np.concatenate(dist_id))]
# accumulate normal forces for each site
sensor = jp.dot((dist > 0) & contacts, forces[:, 0])
diff --git a/mjx/mujoco/mjx/_src/sensor_test.py b/mjx/mujoco/mjx/_src/sensor_test.py
index 9eedac13..19d4ef46 100644
--- a/mjx/mujoco/mjx/_src/sensor_test.py
+++ b/mjx/mujoco/mjx/_src/sensor_test.py
@@ -101,17 +101,14 @@ class SensorTest(parameterized.TestCase):
-
-
-
""")
diff --git a/mjx/mujoco/mjx/test_data/sensor/sensor.xml b/mjx/mujoco/mjx/test_data/sensor/sensor.xml
index c8257831..71532743 100644
--- a/mjx/mujoco/mjx/test_data/sensor/sensor.xml
+++ b/mjx/mujoco/mjx/test_data/sensor/sensor.xml
@@ -102,15 +102,22 @@
-
-
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
@@ -141,7 +148,8 @@
-
+
+
@@ -152,7 +160,8 @@
-
+
+
From a36f2cccb6218c5d069e21236f24d3bb21110778 Mon Sep 17 00:00:00 2001
From: Taylor Howell
Date: Mon, 28 Oct 2024 03:46:00 -0700
Subject: [PATCH 018/426] Add nnz argument for size of allocated memory to
`mju_dense2sparse` and check if this number is too small for number of
non-zeros.
PiperOrigin-RevId: 690545825
Change-Id: I0e31cb907a1151cb2e4766d5f0cdfecb5629a2a6
---
src/engine/engine_util_sparse.c | 15 ++++++--
src/engine/engine_util_sparse.h | 5 +--
test/engine/engine_island_test.cc | 10 +++---
test/engine/engine_util_sparse_test.cc | 48 ++++++++++++++++++++++----
4 files changed, 63 insertions(+), 15 deletions(-)
diff --git a/src/engine/engine_util_sparse.c b/src/engine/engine_util_sparse.c
index 89403bca..f6d05cfd 100644
--- a/src/engine/engine_util_sparse.c
+++ b/src/engine/engine_util_sparse.c
@@ -148,8 +148,13 @@ mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2, int nnz1, const in
// convert matrix from dense to sparse
-void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
- int* rownnz, int* rowadr, int* colind) {
+// nnz is size of res and colind, return 1 if too small, 0 otherwise
+int mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
+ int* rownnz, int* rowadr, int* colind, int nnz) {
+ if (nnz <= 0) {
+ return 1;
+ }
+
int adr = 0;
// find non-zeros and construct sparse
@@ -161,6 +166,11 @@ void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
// find non-zeros
for (int c=0; c < nc; c++) {
if (mat[r*nc+c]) {
+ // check for out of bounds
+ if (adr >= nnz) {
+ return 1;
+ }
+
// record index and count
colind[adr] = c;
rownnz[r]++;
@@ -170,6 +180,7 @@ void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
}
}
}
+ return 0;
}
diff --git a/src/engine/engine_util_sparse.h b/src/engine/engine_util_sparse.h
index ae14cf70..fa2647c3 100644
--- a/src/engine/engine_util_sparse.h
+++ b/src/engine/engine_util_sparse.h
@@ -34,8 +34,9 @@ MJAPI mjtNum mju_dotSparse2(const mjtNum* vec1, const mjtNum* vec2, int nnz1, co
int nnz2, const int* ind2, int flg_unc2);
// convert matrix from dense to sparse
-MJAPI void mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
- int* rownnz, int* rowadr, int* colind);
+// nnz is size of res and colind, return 1 if too small, 0 otherwise
+MJAPI int mju_dense2sparse(mjtNum* res, const mjtNum* mat, int nr, int nc,
+ int* rownnz, int* rowadr, int* colind, int nnz);
// convert matrix from sparse to dense
MJAPI void mju_sparse2dense(mjtNum* res, const mjtNum* mat, int nr, int nc,
diff --git a/test/engine/engine_island_test.cc b/test/engine/engine_island_test.cc
index c0f5afb8..3729e3f0 100644
--- a/test/engine/engine_island_test.cc
+++ b/test/engine/engine_island_test.cc
@@ -55,7 +55,7 @@ TEST_F(IslandTest, FloodFillSingleton) {
int rowadr[nr];
int colind[nnz];
mjtNum res[nnz]; // unused
- mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind);
+ mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind, nnz);
// outputs / scratch
int island[nr];
@@ -82,7 +82,7 @@ TEST_F(IslandTest, FloodFill1) {
int rowadr[nr];
int colind[nnz];
mjtNum res[nnz]; // unused
- mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind);
+ mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind, nnz);
// outputs / stack
int island[nr];
@@ -112,7 +112,7 @@ TEST_F(IslandTest, FloodFill2) {
int rowadr[nr];
int colind[nnz];
mjtNum res[nnz]; // unused
- mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind);
+ mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind, nnz);
// outputs / stack
int island[nr];
@@ -140,7 +140,7 @@ TEST_F(IslandTest, FloodFill3a) {
int rowadr[nr];
int colind[nnz];
mjtNum res[nnz]; // unused
- mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind);
+ mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind, nnz);
// outputs / stack
int island[nr];
@@ -174,7 +174,7 @@ TEST_F(IslandTest, FloodFill3b) {
int rowadr[nr];
int colind[nnz];
mjtNum res[nnz]; // unused
- mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind);
+ mju_dense2sparse(res, mat, nr, nr, rownnz, rowadr, colind, nnz);
// outputs / stack
int island[nr];
diff --git a/test/engine/engine_util_sparse_test.cc b/test/engine/engine_util_sparse_test.cc
index 92b8fc8d..ade96c0b 100644
--- a/test/engine/engine_util_sparse_test.cc
+++ b/test/engine/engine_util_sparse_test.cc
@@ -984,7 +984,7 @@ TEST_F(EngineUtilSparseTest, MjuCholFactorNNZ) {
int rowadrA[2];
int colindA[4];
int rownnzA_factor[2];
- mju_dense2sparse(sparseA, matA, nA, nA, rownnzA, rowadrA, colindA);
+ mju_dense2sparse(sparseA, matA, nA, nA, rownnzA, rowadrA, colindA, 4);
int nnzA = mju_cholFactorNNZ(rownnzA_factor,
rownnzA, rowadrA, colindA, nA, d);
@@ -1000,7 +1000,7 @@ TEST_F(EngineUtilSparseTest, MjuCholFactorNNZ) {
int rowadrB[3];
int colindB[9];
int rownnzB_factor[3];
- mju_dense2sparse(sparseB, matB, nB, nB, rownnzB, rowadrB, colindB);
+ mju_dense2sparse(sparseB, matB, nB, nB, rownnzB, rowadrB, colindB, 9);
int nnzB = mju_cholFactorNNZ(rownnzB_factor,
rownnzB, rowadrB, colindB, nB, d);
@@ -1016,7 +1016,7 @@ TEST_F(EngineUtilSparseTest, MjuCholFactorNNZ) {
int rowadrC[3];
int colindC[9];
int rownnzC_factor[3];
- mju_dense2sparse(sparseC, matC, nC, nC, rownnzC, rowadrC, colindC);
+ mju_dense2sparse(sparseC, matC, nC, nC, rownnzC, rowadrC, colindC, 9);
int nnzC = mju_cholFactorNNZ(rownnzC_factor,
rownnzC, rowadrC, colindC, nC, d);
@@ -1033,7 +1033,7 @@ TEST_F(EngineUtilSparseTest, MjuCholFactorNNZ) {
int rowadrD[4];
int colindD[16];
int rownnzD_factor[4];
- mju_dense2sparse(sparseD, matD, nD, nD, rownnzD, rowadrD, colindD);
+ mju_dense2sparse(sparseD, matD, nD, nD, rownnzD, rowadrD, colindD, 16);
int nnzD = mju_cholFactorNNZ(rownnzD_factor,
rownnzD, rowadrD, colindD, nD, d);
@@ -1050,11 +1050,11 @@ TEST_F(EngineUtilSparseTest, MjuMulMatTVec) {
mjtNum mat[] = {1, 2, 0,
0, 3, 4};
- mjtNum mat_sparse[6];
+ mjtNum mat_sparse[4];
int rownnz[2];
int rowadr[2];
int colind[4];
- mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind);
+ mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind, 4);
// multiply: res = mat' * vec
mjtNum vec[] = {5, 6};
@@ -1064,5 +1064,41 @@ TEST_F(EngineUtilSparseTest, MjuMulMatTVec) {
EXPECT_THAT(AsVector(res, 3), ElementsAre(5, 28, 24));
}
+TEST_F(EngineUtilSparseTest, MjuDenseToSparse) {
+ int nr = 2;
+ int nc = 2;
+ mjtNum mat[] = {1, 2,
+ 0, 3};
+
+ mjtNum mat_sparse[4];
+ int rownnz[2];
+ int rowadr[2];
+ int colind[4];
+
+ // nnz == number of non-zeros
+ int status3 =
+ mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind, 3);
+
+ EXPECT_EQ(status3, 0);
+
+ // nnz > number of non-zeros
+ int status4 =
+ mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind, 4);
+
+ EXPECT_EQ(status4, 0);
+
+ // nnz < number of non-zeros
+ int status2 =
+ mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind, 2);
+
+ EXPECT_EQ(status2, 1);
+
+ // nnz == 0
+ int status0 =
+ mju_dense2sparse(mat_sparse, mat, nr, nc, rownnz, rowadr, colind, 0);
+
+ EXPECT_EQ(status0, 1);
+}
+
} // namespace
} // namespace mujoco
From 3c21abc0e544bfafb140f8ef5506f4fa8f6aa616 Mon Sep 17 00:00:00 2001
From: Taylor Howell
Date: Mon, 28 Oct 2024 04:24:11 -0700
Subject: [PATCH 019/426] Add ray intersection with ellipsoid to MJX.
PiperOrigin-RevId: 690557087
Change-Id: Icd2f0be1243574720baeac82b8bdd92e57c6a61c
---
doc/changelog.rst | 1 +
mjx/mujoco/mjx/_src/ray.py | 24 ++++++++++++++++++++++
mjx/mujoco/mjx/test_data/sensor/sensor.xml | 6 ++++++
3 files changed, 31 insertions(+)
diff --git a/doc/changelog.rst b/doc/changelog.rst
index 6aa3b745..436ffce3 100644
--- a/doc/changelog.rst
+++ b/doc/changelog.rst
@@ -20,6 +20,7 @@ MJX
- Added ``apply_ft``, ``jac``, and ``xfrc_accumulate`` as public functions.
- Added ``TOUCH`` sensor.
- Added support for ``eq_active``. Fixes :github:issue:`2173`.
+- Added ray intersection with ellipsoid.
Bug fixes
^^^^^^^^^
diff --git a/mjx/mujoco/mjx/_src/ray.py b/mjx/mujoco/mjx/_src/ray.py
index 2591385a..9335f4e9 100644
--- a/mjx/mujoco/mjx/_src/ray.py
+++ b/mjx/mujoco/mjx/_src/ray.py
@@ -108,6 +108,29 @@ def _ray_capsule(
return x
+def _ray_ellipsoid(
+ size: jax.Array,
+ pnt: jax.Array,
+ vec: jax.Array,
+) -> jax.Array:
+ """Returns the distance at which a ray intersects with an ellipsoid."""
+
+ # invert size^2
+ s = 1 / jp.square(size)
+
+ # (x*lvec+lpnt)' * diag(1/size^2) * (x*lvec+lpnt) = 1
+ svec = s * vec
+ a = svec @ vec
+ b = svec @ pnt
+ c = (s * pnt) @ pnt - 1
+
+ # solve a*x^2 + 2*b*x + c = 0
+ x0, x1 = _ray_quad(a, b, c)
+ x = jp.where(jp.isinf(x0), x1, x0)
+
+ return x
+
+
def _ray_box(
size: jax.Array,
pnt: jax.Array,
@@ -201,6 +224,7 @@ _RAY_FUNC = {
GeomType.PLANE: _ray_plane,
GeomType.SPHERE: _ray_sphere,
GeomType.CAPSULE: _ray_capsule,
+ GeomType.ELLIPSOID: _ray_ellipsoid,
GeomType.BOX: _ray_box,
GeomType.MESH: _ray_mesh,
}
diff --git a/mjx/mujoco/mjx/test_data/sensor/sensor.xml b/mjx/mujoco/mjx/test_data/sensor/sensor.xml
index 71532743..b6d3fa2a 100644
--- a/mjx/mujoco/mjx/test_data/sensor/sensor.xml
+++ b/mjx/mujoco/mjx/test_data/sensor/sensor.xml
@@ -119,6 +119,11 @@
+
+
+
+