Add replicate element to MJCF. Fixes #553
PiperOrigin-RevId: 629393578 Change-Id: Ia1db1b9e47eba8868117b0f7b85edcd0cc1b6d79
This commit is contained in:
committed by
Copybara-Service
parent
6a5bbb5157
commit
26f23066d1
+70
-3
@@ -110,9 +110,9 @@ Meta elements
|
||||
|
||||
These elements are not strictly part of the low-level MJCF format definition, but rather instruct the compiler to
|
||||
perform some operation on the model. A general property of meta-elements is that they disappear from the model upon
|
||||
saving the XML. There are currently four meta-elements in MJCF:
|
||||
saving the XML. There are currently five meta-elements in MJCF:
|
||||
|
||||
- :ref:`include<include>` and :ref:`frame<frame>`, which are outside of the schema.
|
||||
- :ref:`include<include>`, :ref:`frame<frame>`, and :ref:`replicate<replicate>` which are outside of the schema.
|
||||
- :ref:`composite<body-composite>` and :ref:`flexcomp<body-flexcomp>` which are part of the schema, but serve to
|
||||
procedurally generate other MJCF elements.
|
||||
|
||||
@@ -163,6 +163,72 @@ in their direct children. The attributes of the frame meta-element are documente
|
||||
Note that in the saved model, the frame elements have disappeared but their transformation was accumulated with those
|
||||
of their child elements.
|
||||
|
||||
.. _replicate:
|
||||
|
||||
**replicate** (R)
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
The replicate element duplicates the enclosed kinematic tree elements with incremental translational and rotational
|
||||
offsets, adding namespace suffixes to avoid name collisions. Appended suffix strings are integers in the
|
||||
range ``[0...count-1]`` with the minimum number of digits required to represent the total element count (i.e., if
|
||||
replicating 200 times, suffixes will be ``000, 001, ...`` etc). All referencing elements are automatically replicated
|
||||
and namespaced appropriately. Detailed examples of models using replicate can be found in the
|
||||
`model/replicate/ <https://github.com/google-deepmind/mujoco/tree/main/model/replicate>`__ directory.
|
||||
|
||||
.. _replicate-count:
|
||||
|
||||
:at:`count`: :at-val:`int, required`
|
||||
The number of replicas. Must be positive.
|
||||
|
||||
.. _replicate-sep:
|
||||
|
||||
:at:`sep`: :at-val:`string, optional`
|
||||
The namespace separator. This optional string is prepended to the namespace suffix string. Note that for nested
|
||||
replicate elements, the innermost namespace suffixes are appended first.
|
||||
|
||||
.. _replicate-offset:
|
||||
|
||||
:at:`offset`: :at-val:`real(3), optional`
|
||||
Translational offset along the three coordinate axes. In general, the frame of the offset is with respect to the
|
||||
previous replica, except for the first one which is with respect to the replicate element's parent.
|
||||
If there is no rotation, these values are always in the frame of the replicate element's parent.
|
||||
|
||||
.. _replicate-euler:
|
||||
|
||||
:at:`euler`: :at-val:`real(3), optional`
|
||||
Rotation angles around three coordinate axes between two subsequent replicas. The angular units and rotation sequence
|
||||
respect the global :ref:`angle<compiler-angle>` and :ref:`eulerseq<compiler-eulerseq>` settings. Rotation is always
|
||||
with respect to the frame of the previous replica, so total rotation is cumulative.
|
||||
|
||||
.. collapse:: Usage example of replicate
|
||||
|
||||
Loading this model and saving it:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<replicate count="2" offset="0 1 0" euler="90 0 0">
|
||||
<replicate count="2" sep="-" offset="1 0 0" euler="0 90 0">
|
||||
<geom name="Alice" size=".1"/>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
Results in this model:
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom name="Alice-00" size="0.1"/>
|
||||
<geom name="Alice-10" size="0.1" pos="1 0 0" quat="1 0 1 0"/>
|
||||
<geom name="Alice-01" size="0.1" pos="0 1 0" quat="1 1 0 0"/>
|
||||
<geom name="Alice-11" size="0.1" pos="1 1 0" quat="0.5 0.5 0.5 0.5"/>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
.. _include:
|
||||
|
||||
**include** (*)
|
||||
@@ -2766,7 +2832,8 @@ coordinates results in compiler error. See :ref:`CComposite` in the modeling gui
|
||||
geom and 3 orthogonal sliding joints, allowing translation but not rotation. The geom condim and priority attributes
|
||||
are set to 1 by default. This makes the spheres have frictionless contacts with all other geoms (unless the priority
|
||||
of some frictional geom is higher). The user can replace the default sliders with multiple joints of kind="particle"
|
||||
and replace the default sphere with a custom geom.
|
||||
and replace the default sphere with a custom geom. Note that the particle composite type is deprecated and might be
|
||||
removed in a future version. Instead of particle, it is recommended to use :ref:`replicate`.
|
||||
|
||||
The **grid** type creates a 1D or 2D grid of bodies, each having a sphere geom, a sphere site, and 3 orthogonal
|
||||
sliding joints by default. The :el:`pin` sub-element can be used to specify that some bodies should not have joints,
|
||||
|
||||
+27
-20
@@ -7,15 +7,22 @@ Upcoming version (not yet released)
|
||||
|
||||
General
|
||||
^^^^^^^
|
||||
1. Added ``mjModel.mesh_scale``: the scaling applied to asset vertices, as specified in the
|
||||
|
||||
.. youtube:: 5k0_wsIRAFc
|
||||
:align: right
|
||||
:width: 240px
|
||||
|
||||
1. Added the :ref:`replicate<replicate>` to MJCF, a :ref:`meta-element<meta-element>` which permits to repeat a subtree
|
||||
with incremental translational and rotational offsets.
|
||||
2. Added ``mjModel.mesh_scale``: the scaling applied to asset vertices, as specified in the
|
||||
:ref:`scale<asset-mesh-scale>` attribute.
|
||||
2. Added visual properties which are ignored by the native renderer, but can be used by external renderers:
|
||||
3. Added visual properties which are ignored by the native renderer, but can be used by external renderers:
|
||||
|
||||
- :ref:`light/bulbradius<body-light-bulbradius>` attribute and corresponding ``mjModel.light_bulbradius`` field.
|
||||
- :ref:`material/metallic<asset-material-metallic>` attribute and corresponding ``mjModel.material_metallic`` field.
|
||||
- :ref:`material/roughness<asset-material-roughness>` attribute and corresponding ``mjModel.material_roughness``
|
||||
field.
|
||||
3. The type of the ``size`` argument of :ref:`mj_stackAllocNum` and :ref:`mj_stackAllocInt` was changed from ``int``
|
||||
4. The type of the ``size`` argument of :ref:`mj_stackAllocNum` and :ref:`mj_stackAllocInt` was changed from ``int``
|
||||
to ``size_t``.
|
||||
|
||||
MJX
|
||||
@@ -23,33 +30,33 @@ MJX
|
||||
.. admonition:: Breaking API changes
|
||||
:class: attention
|
||||
|
||||
4. Removed deprecated ``mjx.device_get_into`` and ``mjx.device_put`` functions as they lack critical new
|
||||
5. Removed deprecated ``mjx.device_get_into`` and ``mjx.device_put`` functions as they lack critical new
|
||||
functionality.
|
||||
|
||||
**Migration:** Use ``mjx.get_data_into`` instead of ``mjx.device_get_into``, and ``mjx.put_data`` instead of
|
||||
``mjx.device_put``.
|
||||
|
||||
5. Added cylinder plane collisions.
|
||||
6. Added ``efc_type`` to ``mjx.Data`` and ``dim``, ``efc_address`` to ``mjx.Contact``.
|
||||
7. Added ``geom`` to ``mjx.Contact`` and marked ``geom1``, ``geom2`` deprecated.
|
||||
8. Added ``ne``, ``nf``, ``nl``, ``nefc``, and ``ncon`` to ``mjx.Data`` to match ``mujoco.MjData``.
|
||||
9. Given the above added fields, removed ``mjx.get_params``, ``mjx.ncon``, and ``mjx.count_constraints``.
|
||||
10. Changed the way meshes are organized on device to speed up collision detection when a mesh is replicated for many
|
||||
6. Added cylinder plane collisions.
|
||||
7. Added ``efc_type`` to ``mjx.Data`` and ``dim``, ``efc_address`` to ``mjx.Contact``.
|
||||
8. Added ``geom`` to ``mjx.Contact`` and marked ``geom1``, ``geom2`` deprecated.
|
||||
9. Added ``ne``, ``nf``, ``nl``, ``nefc``, and ``ncon`` to ``mjx.Data`` to match ``mujoco.MjData``.
|
||||
10. Given the above added fields, removed ``mjx.get_params``, ``mjx.ncon``, and ``mjx.count_constraints``.
|
||||
11. Changed the way meshes are organized on device to speed up collision detection when a mesh is replicated for many
|
||||
geoms.
|
||||
11. Fixed a bug where capsules might be ignored in broadphase colliision checking.
|
||||
12. Added cylinder collisions using SDFs.
|
||||
13. Added support for all :ref:`condim <coContact>`: 1, 3, 4, 6.
|
||||
14. Add support functions for ``id2name`` and ``name2id``, MJX versions of :ref:`mj_id2name` and :ref:`mj_name2id`.
|
||||
15. Added support for :ref:`gravcomp<body-gravcomp>` and :ref:`actuatorgravcomp<body-joint-actuatorgravcomp>`.
|
||||
16. Fixed a bug in ``mjx.ray`` for sometimes allowed negative distances for ray-mesh tests.
|
||||
12. Fixed a bug where capsules might be ignored in broadphase colliision checking.
|
||||
13. Added cylinder collisions using SDFs.
|
||||
14. Added support for all :ref:`condim <coContact>`: 1, 3, 4, 6.
|
||||
15. Add support functions for ``id2name`` and ``name2id``, MJX versions of :ref:`mj_id2name` and :ref:`mj_name2id`.
|
||||
16. Added support for :ref:`gravcomp<body-gravcomp>` and :ref:`actuatorgravcomp<body-joint-actuatorgravcomp>`.
|
||||
17. Fixed a bug in ``mjx.ray`` for sometimes allowed negative distances for ray-mesh tests.
|
||||
|
||||
Bug fixes
|
||||
^^^^^^^^^
|
||||
17. Defaults of lights were not being saved, now fixed.
|
||||
18. Prevent overwriting of frame names by body names when saving an XML. Bug introduced in 3.1.4.
|
||||
19. Fixed bug in Python binding of :ref:`mj_saveModel`: ``buffer`` argument was documented as optional but was actually
|
||||
18. Defaults of lights were not being saved, now fixed.
|
||||
19. Prevent overwriting of frame names by body names when saving an XML. Bug introduced in 3.1.4.
|
||||
20. Fixed bug in Python binding of :ref:`mj_saveModel`: ``buffer`` argument was documented as optional but was actually
|
||||
not optional.
|
||||
20. Fixed bug that prevented memory allocations larger than 2.15 GB.
|
||||
21. Fixed bug that prevented memory allocations larger than 2.15 GB.
|
||||
|
||||
|
||||
Version 3.1.4 (April 10th, 2024)
|
||||
|
||||
+163
-2211
File diff suppressed because it is too large
Load Diff
@@ -27,20 +27,20 @@
|
||||
|
||||
<default>
|
||||
<geom solimp=".9 .9 .01"/>
|
||||
<default class="object1">
|
||||
<geom type="capsule" material="object1" size="0.1 0.05"/>
|
||||
<default class="capsule">
|
||||
<geom type="capsule" material="capsule" size="0.1 0.05"/>
|
||||
</default>
|
||||
<default class="object2">
|
||||
<geom type="ellipsoid" material="object2" size="0.15 0.1 0.07"/>
|
||||
<default class="ellipsoid">
|
||||
<geom type="ellipsoid" material="ellipsoid" size="0.15 0.1 0.07"/>
|
||||
</default>
|
||||
<default class="object3">
|
||||
<geom type="box" material="object3" size="0.15 0.1 0.05"/>
|
||||
<default class="box">
|
||||
<geom type="box" material="box" size="0.15 0.1 0.05"/>
|
||||
</default>
|
||||
<default class="object4">
|
||||
<geom type="cylinder" material="object4" size="0.1 0.05" condim="4" friction="1 .01 .01"/>
|
||||
<default class="cylinder">
|
||||
<geom type="cylinder" material="cylinder" size="0.1 0.05" condim="4" friction="1 .01 .01"/>
|
||||
</default>
|
||||
<default class="object5">
|
||||
<geom type="sphere" material="object5" size="0.1"/>
|
||||
<default class="sphere">
|
||||
<geom type="sphere" material="sphere" size="0.1"/>
|
||||
</default>
|
||||
<default class="border">
|
||||
<geom type="capsule" size="0.4" rgba=".4 .4 .4 1"/>
|
||||
@@ -56,13 +56,13 @@
|
||||
rgb1="0.6 0.6 0.6" rgb2="0.6 0.6 0.6" markrgb="1 1 1"/>
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".4 .4 .4" rgb2=".6 .6 .6"
|
||||
width="512" height="512"/>
|
||||
<material name='MatPlane' reflectance='0.3' texture="texplane" texrepeat="1 1" texuniform="true"
|
||||
<material name="MatPlane" reflectance="0.3" texture="texplane" texrepeat="1 1" texuniform="true"
|
||||
rgba=".7 .7 .7 1"/>
|
||||
<material name='object1' texture="texgeom" texuniform="true" rgba=".4 .9 .6 1" />
|
||||
<material name='object2' texture="texgeom" texuniform="true" rgba=".4 .6 .9 1" />
|
||||
<material name='object3' texture="texgeom" texuniform="true" rgba=".4 .9 .9 1" />
|
||||
<material name='object4' texture="texgeom" texuniform="true" rgba=".8 .6 .8 1" />
|
||||
<material name='object5' texture="texgeom" texuniform="true" rgba=".9 .1 .1 1" />
|
||||
<material name="capsule" texture="texgeom" texuniform="true" rgba=".4 .9 .6 1" />
|
||||
<material name="ellipsoid" texture="texgeom" texuniform="true" rgba=".4 .6 .9 1" />
|
||||
<material name="box" texture="texgeom" texuniform="true" rgba=".4 .9 .9 1" />
|
||||
<material name="cylinder" texture="texgeom" texuniform="true" rgba=".8 .6 .8 1" />
|
||||
<material name="sphere" texture="texgeom" texuniform="true" rgba=".9 .1 .1 1" />
|
||||
</asset>
|
||||
|
||||
<visual>
|
||||
@@ -84,430 +84,70 @@
|
||||
<geom class="borderpost" pos="3 -3 0"/>
|
||||
<geom class="borderpost" pos="-3 -3 0"/>
|
||||
|
||||
<body pos="-2 -2 1" quat="-0.632456 -0.632456 0.316228 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 -2 2" quat="-0.5547 -0.5547 0.5547 0.27735">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 -2 3" quat="-0.471405 -0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 -2 4" quat="-0.471405 -0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<frame pos="-2 -2 2.5">
|
||||
<replicate count="5" offset="0 1 0" euler="0 180 0">
|
||||
<frame pos="0 0 -1.5">
|
||||
<replicate count="4" offset="0 0 1" euler="0 0 60">
|
||||
<body euler="30 40 0">
|
||||
<freejoint/>
|
||||
<geom class="capsule"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</frame>
|
||||
|
||||
<body pos="-2 -1 1" quat="-0.755929 -0.377964 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 -1 2" quat="-0.632456 -0.316228 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 -1 3" quat="-0.516398 -0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 -1 4" quat="-0.516398 -0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<frame pos="-1 -2 2.5">
|
||||
<replicate count="5" offset="0 1 0" euler="0 180 0">
|
||||
<frame pos="0 0 -1.5">
|
||||
<replicate count="4" offset="0 0 1" euler="0 0 60">
|
||||
<body euler="20 40 60">
|
||||
<freejoint/>
|
||||
<geom class="ellipsoid"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</frame>
|
||||
|
||||
<body pos="-2 0 1" quat="-0.816497 0 0.408248 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 0 2" quat="-0.666667 0 0.666667 0.333333">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 0 3" quat="-0.534522 0 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 0 4" quat="-0.534522 0 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<frame pos="0 -2 3.5">
|
||||
<replicate count="5" offset="0 1 0" euler="0 180 0">
|
||||
<frame pos="0 0 -1.5">
|
||||
<replicate count="4" offset="0 0 1" euler="0 0 60">
|
||||
<body euler="30 70 110">
|
||||
<freejoint/>
|
||||
<geom class="box"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</frame>
|
||||
|
||||
<body pos="-2 1 1" quat="-0.755929 0.377964 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 1 2" quat="-0.632456 0.316228 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 1 3" quat="-0.516398 0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 1 4" quat="-0.516398 0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<frame pos="1 -2 2.5">
|
||||
<replicate count="5" offset="0 1 0" euler="0 180 0">
|
||||
<frame pos="0 0 -1.5">
|
||||
<replicate count="4" offset="0 0 1" euler="0 0 60">
|
||||
<body euler="60 30 0">
|
||||
<freejoint/>
|
||||
<geom class="cylinder"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</frame>
|
||||
|
||||
<body pos="-2 2 1" quat="-0.632456 0.632456 0.316228 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 2 2" quat="-0.5547 0.5547 0.5547 0.27735">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 2 3" quat="-0.471405 0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
<body pos="-2 2 4" quat="-0.471405 0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object1"/>
|
||||
</body>
|
||||
|
||||
<body pos="-1 -2 1" quat="-0.377964 -0.755929 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 -2 2" quat="-0.316228 -0.632456 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 -2 3" quat="-0.258199 -0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 -2 4" quat="-0.258199 -0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
|
||||
<body pos="-1 -1 1" quat="-0.5 -0.5 0.5 0.5">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 -1 2" quat="-0.377964 -0.377964 0.755929 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 -1 3" quat="-0.288675 -0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 -1 4" quat="-0.288675 -0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
|
||||
<body pos="-1 0 1" quat="-0.57735 0 0.57735 0.57735">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 0 2" quat="-0.408248 0 0.816497 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 0 3" quat="-0.301511 0 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 0 4" quat="-0.301511 0 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
|
||||
<body pos="-1 1 1" quat="-0.5 0.5 0.5 0.5">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 1 2" quat="-0.377964 0.377964 0.755929 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 1 3" quat="-0.288675 0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 1 4" quat="-0.288675 0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
|
||||
<body pos="-1 2 1" quat="-0.377964 0.755929 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 2 2" quat="-0.316228 0.632456 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 2 3" quat="-0.258199 0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
<body pos="-1 2 4" quat="-0.258199 0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object2"/>
|
||||
</body>
|
||||
|
||||
<body pos="0 -2 1" quat="0 -0.816497 0.408248 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 -2 2" quat="0 -0.666667 0.666667 0.333333">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 -2 3" quat="0 -0.534522 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 -2 4" quat="0 -0.534522 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
|
||||
<body pos="0 -1 1" quat="0 -0.57735 0.57735 0.57735">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 -1 2" quat="0 -0.408248 0.816497 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 -1 3" quat="0 -0.301511 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 -1 4" quat="0 -0.301511 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
|
||||
<body pos="0.3 0 1" quat="0 0 0.707107 0.707107">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 0 2" quat="0 0 0.894427 0.447214">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 0 3" quat="0 0 0.948683 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 0 4" quat="0 0 0.948683 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
|
||||
<body pos="0 1 1" quat="0 0.57735 0.57735 0.57735">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 1 2" quat="0 0.408248 0.816497 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 1 3" quat="0 0.301511 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 1 4" quat="0 0.301511 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
|
||||
<body pos="0 2 1" quat="0 0.816497 0.408248 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 2 2" quat="0 0.666667 0.666667 0.333333">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 2 3" quat="0 0.534522 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
<body pos="0 2 4" quat="0 0.534522 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object3"/>
|
||||
</body>
|
||||
|
||||
<body pos="1 -2 1" quat="0.377964 -0.755929 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 -2 2" quat="0.316228 -0.632456 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 -2 3" quat="0.258199 -0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 -2 4" quat="0.258199 -0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
|
||||
<body pos="1 -1 1" quat="0.5 -0.5 0.5 0.5">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 -1 2" quat="0.377964 -0.377964 0.755929 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 -1 3" quat="0.288675 -0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 -1 4" quat="0.288675 -0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
|
||||
<body pos="1 0 1" quat="0.57735 0 0.57735 0.57735">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 0 2" quat="0.408248 0 0.816497 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 0 3" quat="0.301511 0 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 0 4" quat="0.301511 0 0.904534 0.301511">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
|
||||
<body pos="1 1 1" quat="0.5 0.5 0.5 0.5">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 1 2" quat="0.377964 0.377964 0.755929 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 1 3" quat="0.288675 0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 1 4" quat="0.288675 0.288675 0.866025 0.288675">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
|
||||
<body pos="1 2 1" quat="0.377964 0.755929 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 2 2" quat="0.316228 0.632456 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 2 3" quat="0.258199 0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
<body pos="1 2 4" quat="0.258199 0.516398 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object4"/>
|
||||
</body>
|
||||
|
||||
<body pos="2 -2 1" quat="0.632456 -0.632456 0.316228 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 -2 2" quat="0.5547 -0.5547 0.5547 0.27735">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 -2 3" quat="0.471405 -0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 -2 4" quat="0.471405 -0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
|
||||
<body pos="2 -1 1" quat="0.755929 -0.377964 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 -1 2" quat="0.632456 -0.316228 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 -1 3" quat="0.516398 -0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 -1 4" quat="0.516398 -0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
|
||||
<body pos="2 0 1" quat="0.816497 0 0.408248 0.408248">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 0 2" quat="0.666667 0 0.666667 0.333333">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 0 3" quat="0.534522 0 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 0 4" quat="0.534522 0 0.801784 0.267261">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
|
||||
<body pos="2 1 1" quat="0.755929 0.377964 0.377964 0.377964">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 1 2" quat="0.632456 0.316228 0.632456 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 1 3" quat="0.516398 0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 1 4" quat="0.516398 0.258199 0.774597 0.258199">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
|
||||
<body pos="2 2 1" quat="0.632456 0.632456 0.316228 0.316228">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 2 2" quat="0.5547 0.5547 0.5547 0.27735">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 2 3" quat="0.471405 0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<body pos="2 2 4" quat="0.471405 0.471405 0.707107 0.235702">
|
||||
<freejoint/>
|
||||
<geom class="object5"/>
|
||||
</body>
|
||||
<frame pos="2 -2 2.5">
|
||||
<replicate count="5" offset="0 1 0">
|
||||
<frame pos="0 0 -1.5">
|
||||
<replicate count="4" offset="0 0 1">
|
||||
<body>
|
||||
<freejoint/>
|
||||
<geom class="sphere"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</frame>
|
||||
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Replicate
|
||||
|
||||
Models in this directory provide usage examples of the `<replicate>` meta-element, see
|
||||
[documentation](https://mujoco.readthedocs.io/en/stable/XMLreference.html#replicate-r).
|
||||
|
||||
The models contain several categories:
|
||||
|
||||
1. Constructing static geometries:
|
||||
- cylinder.xml
|
||||
- bowl.xml
|
||||
- helix.xml
|
||||
- container.xml
|
||||
|
||||
2. Replicating moving bodies:
|
||||
- particle.xml
|
||||
- bunnies.xml
|
||||
- leaves.xml
|
||||
- stonehenge.xml
|
||||
|
||||
3. Replicas that include tendons:
|
||||
- newton_cradle.xml
|
||||
- tendon.xml
|
||||
|
||||
4. Replicas with multiple referencing elements:
|
||||
- references.xml
|
||||
|
||||
[](https://www.youtube.com/watch?v=5k0_wsIRAFc)
|
||||
@@ -0,0 +1,46 @@
|
||||
<mujoco model="Bowl">
|
||||
<statistic meansize=".05"/>
|
||||
|
||||
<worldbody>
|
||||
<light pos=".1 0 .6"/>
|
||||
<light pos="-.1 0 .6"/>
|
||||
<geom type="plane" size="1 1 .01"/>
|
||||
|
||||
<body pos="-.1 0 .04" euler="20 10 0">
|
||||
<freejoint/>
|
||||
<geom type="cylinder" size="0.03 0.002" rgba=".8 0 0 1"/>
|
||||
<replicate count="20" euler="0 0 18">
|
||||
<frame pos="-.025 0 .03" euler="0 15 0">
|
||||
<replicate count="6" euler="0 15 0">
|
||||
<geom type="box" size=".0044 .009 .002" pos="0 0 -.03" rgba=".8 0 0 1"/>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
<body pos="0 0 .02" euler="20 20 0">
|
||||
<freejoint/>
|
||||
<geom type="cylinder" size="0.01 0.0015" rgba="0 .8 0 1"/>
|
||||
<replicate count="20" euler="0 0 18">
|
||||
<frame pos="-.005 0 .03" euler="0 15 0">
|
||||
<replicate count="6" euler="0 15 0">
|
||||
<geom type="box" size=".0044 .006 .0015" pos="0 0 -.03" rgba="0 .8 0 1"/>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
<body pos=".09 0 .04" euler="30 30 0">
|
||||
<freejoint/>
|
||||
<geom type="cylinder" size="0.03 0.002" rgba="0 0 .8 1"/>
|
||||
<replicate count="20" euler="0 0 18">
|
||||
<frame pos="-.025 0 .03" euler="0 15 0">
|
||||
<replicate count="4" euler="0 15 0">
|
||||
<geom type="box" size=".0044 .009 .002" pos="0 0 -.03" rgba="0 0 .8 1"/>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,25 @@
|
||||
<mujoco model="Bunnies">
|
||||
|
||||
<include file="container.xml"/>
|
||||
|
||||
<size memory="1G"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".01"/>
|
||||
|
||||
<asset>
|
||||
<mesh file="bunny.obj"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<replicate count="5" offset=".17 0 0">
|
||||
<replicate count="5" offset="0 .17 0">
|
||||
<replicate count="5" offset="0 0 .17">
|
||||
<body pos="-0.35 -0.35 0.65">
|
||||
<freejoint/>
|
||||
<geom type="mesh" mesh="bunny" rgba="0.8 0.2 0.1 1"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,31 @@
|
||||
<mujoco model="Container">
|
||||
<statistic extent="1.5" meansize=".05"/>
|
||||
|
||||
<visual>
|
||||
<rgba haze="0.15 0.25 0.35 1"/>
|
||||
<map shadowscale="0.5" zfar="40"/>
|
||||
</visual>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="64" height="64"/>
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".2 .3 .4" rgb2=".1 0.15 0.2" width="512" height="512" mark="cross" markrgb=".8 .8 .8"/>
|
||||
<material name="matplane" reflectance="0.3" texture="texplane" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<light directional="true" diffuse=".4 .4 .4" specular="0.1 0.1 0.1" pos="0 0 5" castshadow="false"/>
|
||||
<light diffuse=".6 .6 .6" specular="0.2 0.2 0.2" pos="0 0 4"/>
|
||||
|
||||
<geom name="ground" type="plane" size="0 0 1" pos="0 0 0" quat="1 0 0 0" material="matplane" condim="1"/>
|
||||
|
||||
<body mocap="true" pos="-.1 .05 0" zaxis=".5 0 1">
|
||||
<geom type="capsule" size=".1 .1" group="1" condim="1"/>
|
||||
</body>
|
||||
</worldbody>
|
||||
|
||||
<worldbody>
|
||||
<replicate count="4" euler="0 0 90">
|
||||
<geom type="plane" size=".5 .5 .05" zaxis="1 0 0" pos="-.5 0 -.25"/>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,31 @@
|
||||
<mujoco model="Cylinder">
|
||||
<statistic meansize=".05"/>
|
||||
|
||||
<worldbody>
|
||||
<light pos=".5 0 3"/>
|
||||
<light pos="-.5 0 3"/>
|
||||
<geom type="plane" size="2 2 .01"/>
|
||||
|
||||
<body pos="-.3 0 .3" euler="80 10 0">
|
||||
<freejoint/>
|
||||
<replicate count="30" euler="0 0 12">
|
||||
<geom type="box" pos="0 -.12 0" size=".01315 .005 .1" rgba=".8 0 0 1"/>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
<body pos="0 0 .4" euler="20 20 0">
|
||||
<freejoint/>
|
||||
<replicate count="10" euler="0 0 36">
|
||||
<geom type="box" pos="0 -0.067 0" size=".025 .01 .1" rgba="0 .8 0 1"/>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
<body pos=".3 0 .3" euler="30 30 0">
|
||||
<freejoint/>
|
||||
<replicate count="4" euler="0 0 90">
|
||||
<geom type="box" pos="0.005 -0.05 0" size=".05 .005 .08" rgba="0 0 .8 1"/>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,25 @@
|
||||
<mujoco model="Helix">
|
||||
<statistic meansize=".05"/>
|
||||
|
||||
<worldbody>
|
||||
<light pos=".5 0 3"/>
|
||||
<light pos="-.5 0 3"/>
|
||||
<geom type="plane" size="2 2 .01"/>
|
||||
|
||||
<body pos="0 0 .3">
|
||||
<freejoint/>
|
||||
<replicate count="50" offset="0 0 0.01" euler="0 0 25">
|
||||
<geom type="capsule" size=".01 .05" pos="0 -.23 -.2" euler="0 84.5 0" rgba="0 0 1 1"/>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
|
||||
<body pos=".6 0 .3" euler="0 0 90">
|
||||
<freejoint/>
|
||||
<replicate count="50" offset="0 0 0.01" euler="0 0 15">
|
||||
<geom type="box" pos=".025 -.2 -.2" size=".032 .04 .005" euler="-24 -12 2" rgba=".8 0 0 1"/>
|
||||
</replicate>
|
||||
</body>
|
||||
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,50 @@
|
||||
<mujoco model="Leaves">
|
||||
|
||||
<statistic extent="1.5"/>
|
||||
|
||||
<visual>
|
||||
<rgba haze="0.15 0.25 0.35 1"/>
|
||||
<map shadowscale="0.5" zfar="40"/>
|
||||
</visual>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="64" height="64"/>
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".2 .3 .4" rgb2=".1 0.15 0.2" width="512" height="512" mark="cross" markrgb=".8 .8 .8"/>
|
||||
<material name="matplane" reflectance="0.3" texture="texplane" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<size memory="1G"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".003" density="1.225" viscosity="1.8e-5" integrator="implicitfast"/>
|
||||
|
||||
<visual>
|
||||
<global realtime="0.2"/>
|
||||
</visual>
|
||||
|
||||
<default>
|
||||
<default class="leaf">
|
||||
<geom type="ellipsoid" size=".025 .01 0.001" mass="1e-4" fluidshape="ellipsoid" rgba="0 .4 0 1"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<camera name="Cinematic" pos="0 -1.6 4.5" xyaxes="1 0 0 0 0.6 0.8" mode="trackcom"/>
|
||||
|
||||
<light diffuse="1 1 1" attenuation=".4 .2 0" pos="-1 -1 6" mode="trackcom" dir="1 1 -3"/>
|
||||
<light diffuse="1 1 1" attenuation=".4 .2 0" pos="-.98 -1.02 6" mode="trackcom" dir="1 1 -3"/>
|
||||
<geom name="ground" type="plane" size="5 5 .01" pos="0 0 0" quat="1 0 0 0" material="matplane" condim="1"/>
|
||||
<replicate count="10" offset="0 0 0.07" euler="0 0 30">
|
||||
<replicate count="6" euler="-3 0 0">
|
||||
<frame pos="-.3 -.3 3">
|
||||
<replicate count="5" offset=".05 0 0" euler="50 0 0">
|
||||
<body euler="50 50 0">
|
||||
<freejoint/>
|
||||
<geom class="leaf" pos=".025 0 0" euler="10 0 0"/>
|
||||
<geom class="leaf" pos="-.025 0 0" euler="-14 0 0" mass="1.2e-4"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</frame>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,69 @@
|
||||
<mujoco model="Newton's Cradle">
|
||||
<option timestep="1e-4"/>
|
||||
|
||||
<visual>
|
||||
<global realtime="0.2"/>
|
||||
</visual>
|
||||
|
||||
<asset>
|
||||
<texture type="skybox" builtin="gradient" rgb1="0.3 0.5 0.7" rgb2="0 0 0" width="64" height="64"/>
|
||||
<texture name="texplane" type="2d" builtin="checker" rgb1=".2 .3 .4" rgb2=".1 0.15 0.2" width="512" height="512" mark="cross" markrgb=".8 .8 .8"/>
|
||||
<material name="matplane" reflectance="0.5" texture="texplane" texrepeat="3 3" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<default>
|
||||
<geom friction=".5" solref="-1e8 -0" solimp="0 .01 1e-3"/>
|
||||
<tendon limited="true" range="0 0.146" width="0.001" solreflimit="1e-3 1" rgba="0 0 .7 1"/>
|
||||
<default class="steel ball">
|
||||
<geom size=".02" rgba=".7 .7 0 1" density="8000" friction=".4" solref="-1e8 -0" solimp="0 .01 1e-3"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<light pos=".5 0 1" dir="-1 0 -2"/>
|
||||
<light pos="-.5 0 1" dir="1 0 -2"/>
|
||||
<geom type="plane" size="4 4 .1" material="matplane"/>
|
||||
|
||||
<replicate count="5" offset="-.04 0 0">
|
||||
<site name="a" pos="0 -.04 .2"/>
|
||||
<site name="c" pos="0 .04 .2"/>
|
||||
<body name="equilibrium" pos="0 0 .04">
|
||||
<freejoint/>
|
||||
<geom class="steel ball"/>
|
||||
<site name="b" pos="0 -.008 .018"/>
|
||||
<site name="d" pos="0 .008 .018"/>
|
||||
</body>
|
||||
</replicate>
|
||||
|
||||
<replicate count="2" offset=".04 0 0">
|
||||
<site name="e" pos=".04 -.04 .2"/>
|
||||
<site name="g" pos=".04 .04 .2"/>
|
||||
<body name="perturbed" pos=".12 0 .061" euler="0 -34 0">
|
||||
<freejoint/>
|
||||
<geom class="steel ball"/>
|
||||
<site name="f" pos="0 -.008 .018"/>
|
||||
<site name="h" pos="0 .008 .018"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
|
||||
<tendon>
|
||||
<spatial>
|
||||
<site site="a"/>
|
||||
<site site="b"/>
|
||||
</spatial>
|
||||
<spatial>
|
||||
<site site="c"/>
|
||||
<site site="d"/>
|
||||
</spatial>
|
||||
<spatial>
|
||||
<site site="e"/>
|
||||
<site site="f"/>
|
||||
</spatial>
|
||||
<spatial>
|
||||
<site site="g"/>
|
||||
<site site="h"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
|
||||
</mujoco>
|
||||
@@ -0,0 +1,23 @@
|
||||
<mujoco model="Particle">
|
||||
|
||||
<include file="container.xml"/>
|
||||
|
||||
<size memory="1G"/>
|
||||
|
||||
<option solver="CG" tolerance="1e-6" timestep=".01"/>
|
||||
|
||||
<worldbody>
|
||||
<replicate count="10" offset=".07 0 0">
|
||||
<replicate count="10" offset="0 .07 0">
|
||||
<replicate count="10" offset="0 0 .07">
|
||||
<body pos="-0.35 -0.35 0.65">
|
||||
<joint type="slide" axis="1 0 0" group="3"/>
|
||||
<joint type="slide" axis="0 1 0" group="3"/>
|
||||
<joint type="slide" axis="0 0 1" group="3"/>
|
||||
<geom size="0.025" condim="1" priority="1" rgba="0.8 0.2 0.1 1"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,40 @@
|
||||
<mujoco model="Stonehenge">
|
||||
<statistic meansize="1"/>
|
||||
<worldbody>
|
||||
<light directional="true" pos="-50 -20 10" dir="5 2 -1" diffuse="1 1 1"/>
|
||||
<geom type="plane" size="30 30 .1" rgba="0 1 0 1"/>
|
||||
<geom type="plane" pos="38 0 5" size="10 30 .1" euler="0 -30 0" rgba="0 .8 0 1"/>
|
||||
|
||||
<body name="wrecking ball" pos="45 0 16">
|
||||
<freejoint/>
|
||||
<geom size="5.5"/>
|
||||
</body>
|
||||
|
||||
<replicate count="30" euler="0 0 12">
|
||||
<body pos="0 -15 2">
|
||||
<freejoint/>
|
||||
<geom type="box" size="1 .5 2"/>
|
||||
</body>
|
||||
<body pos="1.5 -15 4.4" euler="0 0 6">
|
||||
<freejoint/>
|
||||
<geom type="box" size="1.5 .5 .4"/>
|
||||
</body>
|
||||
</replicate>
|
||||
|
||||
<replicate count="5" euler="0 0 45">
|
||||
<body pos="-1.2 -10 3">
|
||||
<freejoint/>
|
||||
<geom type="box" size=".9 .5 3"/>
|
||||
</body>
|
||||
<body pos="1.2 -10 3">
|
||||
<freejoint/>
|
||||
<geom type="box" size=".9 .5 3"/>
|
||||
</body>
|
||||
<body pos="0 -10 6.4">
|
||||
<freejoint/>
|
||||
<geom type="box" size="2.4 .5 .4"/>
|
||||
</body>
|
||||
</replicate>
|
||||
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
@@ -0,0 +1,47 @@
|
||||
<mujoco model="Tendon">
|
||||
<option solver="PGS"/>
|
||||
|
||||
<default>
|
||||
<site size=".02" rgba="0 .8 0 1"/>
|
||||
<geom condim="1"/>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<light pos="1 0 4" dir="-1 0 -4"/>
|
||||
<light pos="-1 0 4" dir="1 0 -4"/>
|
||||
<geom type="plane" size="2 2 .01"/>
|
||||
<site name="ceiling" pos="0.01 0.02 2.4"/>
|
||||
|
||||
<body pos="0 0 1.5">
|
||||
<freejoint/>
|
||||
<site name="hook" pos="0 0 .3"/>
|
||||
<site name="bulb"/>
|
||||
<geom name="bulb" size=".3" rgba="0 0 .8 1" density="100" contype="0" conaffinity="0"/>
|
||||
</body>
|
||||
|
||||
<replicate count="18" euler="0 0 20">
|
||||
<body pos=".2 0 1.5">
|
||||
<freejoint/>
|
||||
<site name="a"/>
|
||||
<geom rgba=".8 0 .4 1" size=".03"/>
|
||||
</body>
|
||||
<body pos="1.2 0 1.5">
|
||||
<freejoint/>
|
||||
<site name="b"/>
|
||||
<geom rgba=".8 .4 0 1" size=".1"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
|
||||
<tendon>
|
||||
<spatial springlength="0 0.5" stiffness="10000">
|
||||
<site site="ceiling"/>
|
||||
<site site="hook"/>
|
||||
</spatial>
|
||||
<spatial range="0 0.5">
|
||||
<site site="a"/>
|
||||
<geom geom="bulb" sidesite="bulb"/>
|
||||
<site site="b"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
</mujoco>
|
||||
@@ -111,10 +111,11 @@ const char* mjs_getError(mjSpec* s) {
|
||||
|
||||
|
||||
// Detach body from mjSpec, return 0 if success.
|
||||
int mjs_detachBody(mjSpec* s, const mjsBody* b) {
|
||||
int mjs_detachBody(mjSpec* s, mjsBody* b) {
|
||||
mjCModel* model = static_cast<mjCModel*>(s->element);
|
||||
mjCBody* body = static_cast<mjCBody*>(b->element);
|
||||
*model -= *body;
|
||||
mjs_deleteBody(b);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -778,8 +778,8 @@ MJAPI int mjs_attachBody(mjsFrame* parent, const mjsBody* child,
|
||||
MJAPI int mjs_attachFrame(mjsBody* parent, const mjsFrame* child,
|
||||
const char* prefix, const char* suffix);
|
||||
|
||||
// Detach body from mjSpec, remove all references, return 0 on success.
|
||||
MJAPI int mjs_detachBody(mjSpec* s, const mjsBody* b);
|
||||
// Detach body from mjSpec, remove all references and delete the body, return 0 on success.
|
||||
MJAPI int mjs_detachBody(mjSpec* s, mjsBody* b);
|
||||
|
||||
|
||||
//---------------------------------- Add tree elements ---------------------------------------------
|
||||
|
||||
+21
-13
@@ -230,21 +230,27 @@ mjCModel& mjCModel::operator+=(const mjCModel& other) {
|
||||
// create global lists
|
||||
MakeLists(bodies[0]);
|
||||
CreateObjectLists();
|
||||
ProcessLists();
|
||||
ProcessLists(/*checkrepeat=*/false);
|
||||
|
||||
// copy all elements not in the tree
|
||||
std::map<mjCDef*, int> def_map;
|
||||
int ndefaults = (int)other.defaults.size();
|
||||
for (int i = 0; i < ndefaults; i++) {
|
||||
defaults.push_back(new mjCDef(*other.defaults[i]));
|
||||
if (this != &other) {
|
||||
defaults.push_back(new mjCDef(*other.defaults[i]));
|
||||
}
|
||||
def_map[other.defaults[i]] = i;
|
||||
}
|
||||
if (this != &other) {
|
||||
// do not copy assets for self-attach
|
||||
CopyList(meshes, other.meshes, def_map, defaults);
|
||||
CopyList(skins, other.skins, def_map, defaults);
|
||||
CopyList(hfields, other.hfields, def_map, defaults);
|
||||
CopyList(textures, other.textures, def_map, defaults);
|
||||
CopyList(materials, other.materials, def_map, defaults);
|
||||
CopyList(keys, other.keys, def_map, defaults);
|
||||
}
|
||||
CopyList(flexes, other.flexes, def_map, defaults);
|
||||
CopyList(meshes, other.meshes, def_map, defaults);
|
||||
CopyList(skins, other.skins, def_map, defaults);
|
||||
CopyList(hfields, other.hfields, def_map, defaults);
|
||||
CopyList(textures, other.textures, def_map, defaults);
|
||||
CopyList(materials, other.materials, def_map, defaults);
|
||||
CopyList(pairs, other.pairs, def_map, defaults);
|
||||
CopyList(excludes, other.excludes, def_map, defaults);
|
||||
CopyList(tendons, other.tendons, def_map, defaults);
|
||||
@@ -254,7 +260,6 @@ mjCModel& mjCModel::operator+=(const mjCModel& other) {
|
||||
CopyList(numerics, other.numerics, def_map, defaults);
|
||||
CopyList(texts, other.texts, def_map, defaults);
|
||||
CopyList(tuples, other.tuples, def_map, defaults);
|
||||
CopyList(keys, other.keys, def_map, defaults);
|
||||
|
||||
// plugins are global
|
||||
plugins = other.plugins;
|
||||
@@ -309,6 +314,7 @@ void mjCModel::RemoveFromList(std::vector<T*>& list, const mjCModel& other) {
|
||||
element->id -= removed;
|
||||
try {
|
||||
// check if the element contains an error
|
||||
element->NameSpace(&other);
|
||||
element->CopyFromSpec();
|
||||
element->ResolveReferences(&other);
|
||||
} catch (mjCError err) {
|
||||
@@ -316,6 +322,7 @@ void mjCModel::RemoveFromList(std::vector<T*>& list, const mjCModel& other) {
|
||||
}
|
||||
try {
|
||||
// check if the element references something that was removed
|
||||
element->NameSpace(this);
|
||||
element->ResolveReferences(this);
|
||||
} catch (mjCError err) {
|
||||
delete element;
|
||||
@@ -333,7 +340,7 @@ mjCModel& mjCModel::operator-=(const mjCBody& subtree) {
|
||||
mjCModel oldmodel(*this);
|
||||
oldmodel.MakeLists(oldmodel.bodies[0]);
|
||||
oldmodel.CreateObjectLists();
|
||||
oldmodel.ProcessLists();
|
||||
oldmodel.ProcessLists(/*checkrepeat=*/false);
|
||||
|
||||
// remove body from tree
|
||||
*bodies[0] -= subtree;
|
||||
@@ -341,7 +348,7 @@ mjCModel& mjCModel::operator-=(const mjCBody& subtree) {
|
||||
// create global lists
|
||||
MakeLists(bodies[0]);
|
||||
CreateObjectLists();
|
||||
ProcessLists();
|
||||
ProcessLists(/*checkrepeat=*/false);
|
||||
|
||||
// check if we have to remove anything else
|
||||
RemoveFromList(pairs, oldmodel);
|
||||
@@ -3044,15 +3051,16 @@ static void processlist(mjListKeyMap& ids, vector<T*>& list,
|
||||
|
||||
|
||||
// set object ids, check for repeated names
|
||||
void mjCModel::ProcessLists() {
|
||||
void mjCModel::ProcessLists(bool checkrepeat) {
|
||||
for (int i = 0; i < mjNOBJECT; i++) {
|
||||
if (i != mjOBJ_XBODY && object_lists[i]) {
|
||||
processlist(ids, *object_lists[i], (mjtObj) i);
|
||||
ids[i].clear();
|
||||
processlist(ids, *object_lists[i], (mjtObj) i, checkrepeat);
|
||||
}
|
||||
}
|
||||
|
||||
// check repeated names in meta elements
|
||||
processlist(ids, frames, mjOBJ_FRAME);
|
||||
processlist(ids, frames, mjOBJ_FRAME, checkrepeat);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -307,7 +307,7 @@ class mjCModel : public mjCModel_, private mjSpec {
|
||||
void CreateObjectLists();
|
||||
|
||||
// populate objects ids
|
||||
void ProcessLists();
|
||||
void ProcessLists(bool checkrepeat = true);
|
||||
|
||||
mjListKeyMap ids; // map from object names to ids
|
||||
mjCError errInfo; // last error info
|
||||
|
||||
+175
-60
@@ -728,6 +728,9 @@ mjCBody& mjCBody::operator=(const mjCBody& other) {
|
||||
lights.clear();
|
||||
id = other.id;
|
||||
|
||||
// copy defaults
|
||||
def = other.def;
|
||||
|
||||
// add elements to lists
|
||||
*this += other;
|
||||
}
|
||||
@@ -797,18 +800,17 @@ mjCBody& mjCBody::operator+=(const mjCFrame& other) {
|
||||
CopyList(cameras, subtree->cameras, fmap, &other);
|
||||
CopyList(lights, subtree->lights, fmap, &other);
|
||||
|
||||
for (int i=0; i<subtree->bodies.size(); i++) {
|
||||
int nbodies = (int)subtree->bodies.size();
|
||||
for (int i=0; i<nbodies; i++) {
|
||||
if (!other.IsAncestor(subtree->bodies[i]->frame)) {
|
||||
continue;
|
||||
}
|
||||
bodies.push_back(new mjCBody(*subtree->bodies[i], model)); // triggers recursive call
|
||||
bodies.back()->frame =
|
||||
subtree->bodies[i]->frame ? frames[fmap[subtree->bodies[i]->frame]] : nullptr;
|
||||
bodies.back()->NameSpace_(other.model, /*propagate=*/ false);
|
||||
}
|
||||
|
||||
// name space
|
||||
this->NameSpace(other.model);
|
||||
|
||||
// attach referencing elements
|
||||
*model += *other.model;
|
||||
|
||||
@@ -832,14 +834,14 @@ void mjCBody::CopyList(std::vector<T*>& dst, const std::vector<T*>& src,
|
||||
dst.push_back(new T(*src[i]));
|
||||
dst.back()->body = this;
|
||||
dst.back()->model = model;
|
||||
dst.back()->id = -1;
|
||||
dst.back()->def = src[i]->def;
|
||||
|
||||
// assign dst frame to src frame
|
||||
dst.back()->frame = src[i]->frame ? frames[fmap[src[i]->frame]] : nullptr;
|
||||
|
||||
// set namespace from pframe if given
|
||||
if (pframe) {
|
||||
dst.back()->NameSpace(pframe->model);
|
||||
}
|
||||
// set namespace
|
||||
dst.back()->NameSpace(src[i]->model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,8 +908,25 @@ mjCBody::~mjCBody() {
|
||||
|
||||
// apply prefix and suffix, propagate to children
|
||||
void mjCBody::NameSpace(const mjCModel* m) {
|
||||
NameSpace_(m, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// apply prefix and suffix, propagate to all descendents or only to child bodies
|
||||
void mjCBody::NameSpace_(const mjCModel* m, bool propagate) {
|
||||
if (!name.empty()) {
|
||||
name = prefix + name + suffix;
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
|
||||
for (auto& body : bodies) {
|
||||
body->prefix = m->prefix;
|
||||
body->suffix = m->suffix;
|
||||
body->NameSpace_(m, propagate);
|
||||
}
|
||||
|
||||
if (!propagate) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& joint : joints) {
|
||||
@@ -929,12 +948,6 @@ void mjCBody::NameSpace(const mjCModel* m) {
|
||||
for (auto& light : lights) {
|
||||
light->NameSpace(m);
|
||||
}
|
||||
|
||||
for (auto& body : bodies) {
|
||||
body->prefix = m->prefix;
|
||||
body->suffix = m->suffix;
|
||||
body->NameSpace(m);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1836,6 +1849,23 @@ void mjCGeom::CopyFromSpec() {
|
||||
|
||||
|
||||
|
||||
void mjCGeom::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
if (!spec_material_.empty() && model != m) {
|
||||
spec_material_ = m->prefix + spec_material_ + m->suffix;
|
||||
}
|
||||
if (!spec_hfieldname_.empty() && model != m) {
|
||||
spec_hfieldname_ = m->prefix + spec_hfieldname_ + m->suffix;
|
||||
}
|
||||
if (!spec_meshname_.empty() && model != m) {
|
||||
spec_meshname_ = m->prefix + spec_meshname_ + m->suffix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compute geom volume
|
||||
double mjCGeom::GetVolume(void) {
|
||||
double height;
|
||||
@@ -2196,7 +2226,7 @@ void mjCGeom::Compile(void) {
|
||||
}
|
||||
|
||||
// check hfield
|
||||
if ((type==mjGEOM_HFIELD && !hfield) || (type!=mjGEOM_HFIELD && hfield)) {
|
||||
if ((type==mjGEOM_HFIELD && !hfield) || (type != mjGEOM_HFIELD && hfield)) {
|
||||
throw mjCError(this, "hfield geom '%s' (id = %d) must have valid hfieldid", name.c_str(), id);
|
||||
}
|
||||
|
||||
@@ -2272,7 +2302,7 @@ void mjCGeom::Compile(void) {
|
||||
|
||||
// fit geom if type is not mjGEOM_MESH
|
||||
double meshpos[3];
|
||||
if (type!=mjGEOM_MESH && type!=mjGEOM_SDF) {
|
||||
if (type != mjGEOM_MESH && type != mjGEOM_SDF) {
|
||||
mesh->FitGeom(this, meshpos);
|
||||
|
||||
// remove reference to mesh
|
||||
@@ -2578,7 +2608,9 @@ void mjCCamera::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
spec_targetbody_ = m->prefix + spec_targetbody_ + m->suffix;
|
||||
if (!spec_targetbody_.empty()) {
|
||||
spec_targetbody_ = m->prefix + spec_targetbody_ + m->suffix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2726,7 +2758,9 @@ void mjCLight::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
spec_targetbody_ = m->prefix + spec_targetbody_ + m->suffix;
|
||||
if (!spec_targetbody_.empty()) {
|
||||
spec_targetbody_ = m->prefix + spec_targetbody_ + m->suffix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3515,7 +3549,7 @@ void mjCTexture::LoadCubeSingle(string filename, const mjVFS* vfs) {
|
||||
LoadFlip(filename, vfs, image, w, h);
|
||||
|
||||
// check gridsize for compatibility
|
||||
if (w/gridsize[1]!=h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
|
||||
if (w/gridsize[1] != h/gridsize[0] || (w%gridsize[1]) || (h%gridsize[0])) {
|
||||
throw mjCError(this,
|
||||
"PNG size must be integer multiple of gridsize in texture '%s' (id %d)",
|
||||
(const char*)file_.c_str(), id);
|
||||
@@ -3621,7 +3655,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
||||
LoadFlip(filename, vfs, image, w, h);
|
||||
|
||||
// PNG must be square
|
||||
if (w!=h) {
|
||||
if (w != h) {
|
||||
throw mjCError(this,
|
||||
"Non-square PNG file '%s' in cube or skybox id %d",
|
||||
(const char*)cubefiles_[i].c_str(), id);
|
||||
@@ -3638,7 +3672,7 @@ void mjCTexture::LoadCubeSeparate(const mjVFS* vfs) {
|
||||
}
|
||||
|
||||
// otherwise check size
|
||||
else if (width!=w) {
|
||||
else if (width != w) {
|
||||
throw mjCError(this,
|
||||
"PNG file '%s' has incompatible size in texture id %d",
|
||||
(const char*)cubefiles_[i].c_str(), id);
|
||||
@@ -3674,14 +3708,14 @@ void mjCTexture::Compile(const mjVFS* vfs) {
|
||||
CopyFromSpec();
|
||||
|
||||
// builtin
|
||||
if (builtin!=mjBUILTIN_NONE) {
|
||||
if (builtin != mjBUILTIN_NONE) {
|
||||
// check size
|
||||
if (width<1 || height<1) {
|
||||
throw mjCError(this, "Invalid width or height of builtin texture");
|
||||
}
|
||||
|
||||
// adjust height of cube texture
|
||||
if (type!=mjTEXTURE_2D) {
|
||||
if (type != mjTEXTURE_2D) {
|
||||
height = 6*width;
|
||||
}
|
||||
|
||||
@@ -3815,6 +3849,17 @@ void mjCMaterial::CopyFromSpec() {
|
||||
|
||||
|
||||
|
||||
void mjCMaterial::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
if (!spec_texture_.empty() && model != m) {
|
||||
spec_texture_ = m->prefix + spec_texture_ + m->suffix;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// compiler
|
||||
void mjCMaterial::Compile(void) {
|
||||
CopyFromSpec();
|
||||
@@ -3891,8 +3936,8 @@ void mjCPair::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
spec_geomname1_ = m->prefix + spec_geomname1_ + m->suffix;
|
||||
spec_geomname2_ = m->prefix + spec_geomname2_ + m->suffix;
|
||||
prefix = m->prefix;
|
||||
suffix = m->suffix;
|
||||
}
|
||||
|
||||
|
||||
@@ -3908,18 +3953,32 @@ void mjCPair::CopyFromSpec() {
|
||||
|
||||
|
||||
void mjCPair::ResolveReferences(const mjCModel* m) {
|
||||
// find geom 1
|
||||
geomname1_ = prefix + geomname1_ + suffix;
|
||||
geomname2_ = prefix + geomname2_ + suffix;
|
||||
geom1 = (mjCGeom*)m->FindObject(mjOBJ_GEOM, geomname1_);
|
||||
geom2 = (mjCGeom*)m->FindObject(mjOBJ_GEOM, geomname2_);
|
||||
|
||||
if (!geom1 && geom2) {
|
||||
geomname1_ = spec_geomname1_;
|
||||
geom1 = (mjCGeom*)m->FindObject(mjOBJ_GEOM, geomname1_);
|
||||
}
|
||||
if (geom1 && !geom2) {
|
||||
geomname2_ = spec_geomname2_;
|
||||
geom2 = (mjCGeom*)m->FindObject(mjOBJ_GEOM, geomname2_);
|
||||
}
|
||||
|
||||
if (!geom1) {
|
||||
throw mjCError(this, "geom '%s' not found in collision %d", geomname1_.c_str(), id);
|
||||
}
|
||||
|
||||
// find geom 2
|
||||
geom2 = (mjCGeom*)m->FindObject(mjOBJ_GEOM, geomname2_);
|
||||
if (!geom2) {
|
||||
throw mjCError(this, "geom '%s' not found in collision %d", geomname2_.c_str(), id);
|
||||
}
|
||||
|
||||
spec_geomname1_ = geomname1_;
|
||||
spec_geomname2_ = geomname2_;
|
||||
prefix.clear();
|
||||
suffix.clear();
|
||||
|
||||
// swap if body1 > body2
|
||||
if (geom1->body->id > geom2->body->id) {
|
||||
string nametmp = geomname1_;
|
||||
@@ -3964,7 +4023,7 @@ void mjCPair::Compile(void) {
|
||||
}
|
||||
|
||||
// set undefined condim, friction, solref, solimp: different priority
|
||||
if (geom1->priority!=geom2->priority) {
|
||||
if (geom1->priority != geom2->priority) {
|
||||
mjCGeom* pgh = (geom1->priority>geom2->priority ? geom1 : geom2);
|
||||
|
||||
// condim
|
||||
@@ -4101,8 +4160,8 @@ void mjCBodyPair::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
spec_bodyname1_ = m->prefix + spec_bodyname1_ + m->suffix;
|
||||
spec_bodyname2_ = m->prefix + spec_bodyname2_ + m->suffix;
|
||||
prefix = m->prefix;
|
||||
suffix = m->suffix;
|
||||
}
|
||||
|
||||
|
||||
@@ -4118,18 +4177,32 @@ void mjCBodyPair::CopyFromSpec() {
|
||||
|
||||
|
||||
void mjCBodyPair::ResolveReferences(const mjCModel* m) {
|
||||
// find body 1
|
||||
bodyname1_ = prefix + bodyname1_ + suffix;
|
||||
bodyname2_ = prefix + bodyname2_ + suffix;
|
||||
mjCBody* pb1 = (mjCBody*)m->FindObject(mjOBJ_BODY, bodyname1_);
|
||||
mjCBody* pb2 = (mjCBody*)m->FindObject(mjOBJ_BODY, bodyname2_);
|
||||
|
||||
if (!pb1 && pb2) {
|
||||
bodyname1_ = spec_bodyname1_;
|
||||
pb1 = (mjCBody*)m->FindObject(mjOBJ_BODY, bodyname1_);
|
||||
}
|
||||
if (pb1 && !pb2) {
|
||||
bodyname2_ = spec_bodyname2_;
|
||||
pb2 = (mjCBody*)m->FindObject(mjOBJ_BODY, bodyname2_);
|
||||
}
|
||||
|
||||
if (!pb1) {
|
||||
throw mjCError(this, "body '%s' not found in bodypair %d", bodyname1_.c_str(), id);
|
||||
}
|
||||
|
||||
// find body 2
|
||||
mjCBody* pb2 = (mjCBody*)m->FindObject(mjOBJ_BODY, bodyname2_);
|
||||
if (!pb2) {
|
||||
throw mjCError(this, "body '%s' not found in bodypair %d", bodyname2_.c_str(), id);
|
||||
}
|
||||
|
||||
spec_bodyname1_ = bodyname1_;
|
||||
spec_bodyname2_ = bodyname2_;
|
||||
prefix.clear();
|
||||
suffix.clear();
|
||||
|
||||
// swap if body1 > body2
|
||||
if (pb1->id > pb2->id) {
|
||||
string nametmp = bodyname1_;
|
||||
@@ -4391,9 +4464,8 @@ void mjCTendon::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
for (int i=0; i<path.size(); i++) {
|
||||
path[i]->NameSpace(m);
|
||||
}
|
||||
prefix = m->prefix;
|
||||
suffix = m->suffix;
|
||||
}
|
||||
|
||||
|
||||
@@ -4517,9 +4589,28 @@ mjCWrap* mjCTendon::GetWrap(int id) {
|
||||
|
||||
|
||||
void mjCTendon::ResolveReferences(const mjCModel* m) {
|
||||
int nfailure = 0;
|
||||
for (int i=0; i<path.size(); i++) {
|
||||
path[i]->ResolveReferences(m);
|
||||
std::string pname = path[i]->name;
|
||||
std::string psidesite = path[i]->sidesite;
|
||||
try {
|
||||
// look for wrapped element with namespace
|
||||
path[i]->name = prefix + pname + suffix;
|
||||
path[i]->sidesite = prefix + psidesite + suffix;
|
||||
path[i]->ResolveReferences(m);
|
||||
} catch(mjCError) {
|
||||
// remove namespace from wrap names
|
||||
path[i]->name = pname;
|
||||
path[i]->sidesite = psidesite;
|
||||
path[i]->ResolveReferences(m);
|
||||
nfailure++;
|
||||
}
|
||||
}
|
||||
if (nfailure==path.size()) {
|
||||
throw mjCError(this, "tendon '%s' (id = %d): no attached reference found", name.c_str(), id);
|
||||
}
|
||||
prefix.clear();
|
||||
suffix.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -4543,7 +4634,7 @@ void mjCTendon::Compile(void) {
|
||||
}
|
||||
|
||||
// determine type
|
||||
bool spatial = (path[0]->type!=mjWRAP_JOINT);
|
||||
bool spatial = (path[0]->type != mjWRAP_JOINT);
|
||||
|
||||
// require at least two objects in spatial path
|
||||
if (spatial && sz<2) {
|
||||
@@ -4564,7 +4655,7 @@ void mjCTendon::Compile(void) {
|
||||
// fixed
|
||||
if (!spatial) {
|
||||
// make sure all objects are joints
|
||||
if (path[i]->type!=mjWRAP_JOINT) {
|
||||
if (path[i]->type != mjWRAP_JOINT) {
|
||||
throw mjCError(this, "tendon '%s' (id = %d): spatial object found in fixed path at pos %d",
|
||||
name.c_str(), id, i);
|
||||
}
|
||||
@@ -4607,7 +4698,7 @@ void mjCTendon::Compile(void) {
|
||||
case mjWRAP_SPHERE:
|
||||
case mjWRAP_CYLINDER:
|
||||
// geom must be bracketed by sites
|
||||
if (i==0 || i==sz-1 || path[i-1]->type!=mjWRAP_SITE || path[i+1]->type!=mjWRAP_SITE) {
|
||||
if (i==0 || i==sz-1 || path[i-1]->type != mjWRAP_SITE || path[i+1]->type != mjWRAP_SITE) {
|
||||
throw mjCError(this,
|
||||
"tendon '%s' (id = %d): geom at pos %d not bracketed by sites",
|
||||
name.c_str(), id, i);
|
||||
@@ -5187,8 +5278,8 @@ void mjCSensor::NameSpace(const mjCModel* m) {
|
||||
if (!name.empty()) {
|
||||
name = m->prefix + name + m->suffix;
|
||||
}
|
||||
spec_objname_ = m->prefix + spec_objname_ + m->suffix;
|
||||
spec_refname_ = m->prefix + spec_refname_ + m->suffix;
|
||||
prefix = m->prefix;
|
||||
suffix = m->suffix;
|
||||
}
|
||||
|
||||
|
||||
@@ -5210,15 +5301,35 @@ void mjCSensor::CopyFromSpec() {
|
||||
|
||||
|
||||
void mjCSensor::ResolveReferences(const mjCModel* m) {
|
||||
objname_ = prefix + objname_ + suffix;
|
||||
refname_ = prefix + refname_ + suffix;
|
||||
|
||||
// get references using the namespace
|
||||
if (objtype != mjOBJ_UNKNOWN) {
|
||||
obj = m->FindObject(objtype, objname_);
|
||||
}
|
||||
if (reftype != mjOBJ_UNKNOWN) {
|
||||
ref = m->FindObject(reftype, refname_);
|
||||
}
|
||||
|
||||
// if failure and both were requested, use namespace only on one
|
||||
if (objtype != mjOBJ_UNKNOWN && reftype != mjOBJ_UNKNOWN && !obj && ref) {
|
||||
objname_ = spec_objname_;
|
||||
obj = m->FindObject(objtype, objname_);
|
||||
}
|
||||
if (objtype != mjOBJ_UNKNOWN && reftype != mjOBJ_UNKNOWN && obj && !ref) {
|
||||
refname_ = spec_refname_;
|
||||
ref = m->FindObject(reftype, refname_);
|
||||
}
|
||||
|
||||
// get objid from objtype and objname
|
||||
if (objtype!=mjOBJ_UNKNOWN) {
|
||||
if (objtype != mjOBJ_UNKNOWN) {
|
||||
// check for missing object name
|
||||
if (objname_.empty()) {
|
||||
throw mjCError(this, "missing name of sensorized object in sensor");
|
||||
}
|
||||
|
||||
// find name
|
||||
obj = m->FindObject(objtype, objname_);
|
||||
if (!obj) {
|
||||
throw mjCError(this, "unrecognized name '%s' of sensorized object", objname_.c_str());
|
||||
}
|
||||
@@ -5234,21 +5345,20 @@ void mjCSensor::ResolveReferences(const mjCModel* m) {
|
||||
}
|
||||
|
||||
// get refid from reftype and refname
|
||||
if (reftype!=mjOBJ_UNKNOWN) {
|
||||
if (reftype != mjOBJ_UNKNOWN) {
|
||||
// check for missing object name
|
||||
if (refname_.empty()) {
|
||||
throw mjCError(this, "missing name of reference frame object in sensor");
|
||||
}
|
||||
|
||||
// find name
|
||||
ref = m->FindObject(reftype, refname_);
|
||||
if (!ref) {
|
||||
throw mjCError(this, "unrecognized name '%s' of reference frame object", refname_.c_str());
|
||||
}
|
||||
|
||||
// must be attached to object with spatial frame
|
||||
if (reftype!=mjOBJ_BODY && reftype!=mjOBJ_XBODY &&
|
||||
reftype!=mjOBJ_GEOM && reftype!=mjOBJ_SITE && reftype!=mjOBJ_CAMERA) {
|
||||
if (reftype != mjOBJ_BODY && reftype != mjOBJ_XBODY &&
|
||||
reftype != mjOBJ_GEOM && reftype != mjOBJ_SITE && reftype != mjOBJ_CAMERA) {
|
||||
throw mjCError(this,
|
||||
"reference frame object must be (x)body, geom, site or camera in sensor");
|
||||
}
|
||||
@@ -5256,6 +5366,11 @@ void mjCSensor::ResolveReferences(const mjCModel* m) {
|
||||
// get sensorized object id
|
||||
refid = ref->id;
|
||||
}
|
||||
|
||||
spec_objname_ = objname_;
|
||||
spec_refname_ = refname_;
|
||||
prefix.clear();
|
||||
suffix.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -5537,7 +5652,7 @@ void mjCSensor::Compile(void) {
|
||||
}
|
||||
|
||||
// make sure dim is consistent with datatype
|
||||
if (datatype==mjDATATYPE_AXIS && dim!=3) {
|
||||
if (datatype==mjDATATYPE_AXIS && dim != 3) {
|
||||
throw mjCError(this,
|
||||
"datatype AXIS requires dim=3 in sensor");
|
||||
}
|
||||
@@ -5843,7 +5958,7 @@ void mjCTuple::ResolveReferences(const mjCModel* m) {
|
||||
}
|
||||
|
||||
// check for size conflict
|
||||
if (objtype_.size()!=objname_.size() || objtype_.size()!=objprm_.size()) {
|
||||
if (objtype_.size() != objname_.size() || objtype_.size() != objprm_.size()) {
|
||||
throw mjCError(this,
|
||||
"tuple '%s' (id = %d) has object arrays with different sizes", name.c_str(), id);
|
||||
}
|
||||
@@ -5984,7 +6099,7 @@ void mjCKey::Compile(const mjModel* m) {
|
||||
for (int i=0; i<m->nq; i++) {
|
||||
qpos_[i] = (double)m->qpos0[i];
|
||||
}
|
||||
} else if (qpos_.size()!=m->nq) {
|
||||
} else if (qpos_.size() != m->nq) {
|
||||
throw mjCError(this, "key %d: invalid qpos size, expected length %d", nullptr, id, m->nq);
|
||||
}
|
||||
|
||||
@@ -5994,7 +6109,7 @@ void mjCKey::Compile(const mjModel* m) {
|
||||
for (int i=0; i<m->nv; i++) {
|
||||
qvel_[i] = 0;
|
||||
}
|
||||
} else if (qvel_.size()!=m->nv) {
|
||||
} else if (qvel_.size() != m->nv) {
|
||||
throw mjCError(this, "key %d: invalid qvel size, expected length %d", nullptr, id, m->nv);
|
||||
}
|
||||
|
||||
@@ -6004,7 +6119,7 @@ void mjCKey::Compile(const mjModel* m) {
|
||||
for (int i=0; i<m->na; i++) {
|
||||
act_[i] = 0;
|
||||
}
|
||||
} else if (act_.size()!=m->na) {
|
||||
} else if (act_.size() != m->na) {
|
||||
throw mjCError(this, "key %d: invalid act size, expected length %d", nullptr, id, m->na);
|
||||
}
|
||||
|
||||
@@ -6021,7 +6136,7 @@ void mjCKey::Compile(const mjModel* m) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mpos_.size()!=3*m->nmocap) {
|
||||
} else if (mpos_.size() != 3*m->nmocap) {
|
||||
throw mjCError(this, "key %d: invalid mpos size, expected length %d", nullptr, id, 3*m->nmocap);
|
||||
}
|
||||
|
||||
@@ -6039,7 +6154,7 @@ void mjCKey::Compile(const mjModel* m) {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mquat_.size()!=4*m->nmocap) {
|
||||
} else if (mquat_.size() != 4*m->nmocap) {
|
||||
throw mjCError(this, "key %d: invalid mquat size, expected length %d", nullptr, id, 4*m->nmocap);
|
||||
}
|
||||
|
||||
@@ -6049,7 +6164,7 @@ void mjCKey::Compile(const mjModel* m) {
|
||||
for (int i=0; i<m->nu; i++) {
|
||||
ctrl_[i] = 0;
|
||||
}
|
||||
} else if (ctrl_.size()!=m->nu) {
|
||||
} else if (ctrl_.size() != m->nu) {
|
||||
throw mjCError(this, "key %d: invalid ctrl size, expected length %d", nullptr, id, m->nu);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +322,7 @@ class mjCBody : public mjCBody_, private mjsBody {
|
||||
|
||||
void CopyFromSpec(); // copy spec into attributes
|
||||
void PointToLocal(void);
|
||||
void NameSpace_(const mjCModel* m, bool propagate = true);
|
||||
|
||||
// copy src list of elements into dst; set body, model and frame
|
||||
template <typename T>
|
||||
@@ -496,6 +497,7 @@ class mjCGeom : public mjCGeom_, private mjsGeom {
|
||||
void ComputeAABB(void); // compute axis-aligned bounding box
|
||||
void CopyFromSpec(void);
|
||||
void PointToLocal(void);
|
||||
void NameSpace(const mjCModel* m);
|
||||
|
||||
// inherited
|
||||
using mjCBase::classname;
|
||||
@@ -1080,6 +1082,7 @@ class mjCMaterial : public mjCMaterial_, private mjsMaterial {
|
||||
|
||||
void CopyFromSpec();
|
||||
void PointToLocal();
|
||||
void NameSpace(const mjCModel* m);
|
||||
|
||||
std::string get_texture() { return texture_; }
|
||||
void del_texture() { texture_.clear(); }
|
||||
|
||||
@@ -74,6 +74,16 @@ void ReadPluginConfigs(tinyxml2::XMLElement* elem, mjsPlugin* p) {
|
||||
mjs_setPluginAttributes(p, &config_attribs);
|
||||
}
|
||||
}
|
||||
|
||||
static void UpdateString(std::string& psuffix, int count, int i) {
|
||||
int ndigits = std::to_string(count).length();
|
||||
std::string i_string = std::to_string(i);
|
||||
std::string prefix = "";
|
||||
while (ndigits-- > i_string.length()) {
|
||||
prefix += '0';
|
||||
}
|
||||
psuffix += prefix + i_string;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -870,11 +880,6 @@ void mjXReader::Parse(XMLElement* root) {
|
||||
Asset(section);
|
||||
}
|
||||
|
||||
for (XMLElement* section = FirstChildElement(root, "worldbody"); section;
|
||||
section = NextSiblingElement(section, "worldbody")) {
|
||||
Body(section, mjs_findBody(model, "world"), nullptr);
|
||||
}
|
||||
|
||||
for (XMLElement* section = FirstChildElement(root, "contact"); section;
|
||||
section = NextSiblingElement(section, "contact")) {
|
||||
Contact(section);
|
||||
@@ -909,6 +914,11 @@ void mjXReader::Parse(XMLElement* root) {
|
||||
section = NextSiblingElement(section, "keyframe")) {
|
||||
Keyframe(section);
|
||||
}
|
||||
|
||||
for (XMLElement* section = FirstChildElement(root, "worldbody"); section;
|
||||
section = NextSiblingElement(section, "worldbody")) {
|
||||
Body(section, mjs_findBody(model, "world"), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3371,6 +3381,72 @@ void mjXReader::Body(XMLElement* section, mjsBody* pbody, mjsFrame* frame) {
|
||||
Body(elem, pbody, pframe);
|
||||
}
|
||||
|
||||
// replicate sub-element
|
||||
else if (name=="replicate") {
|
||||
int count;
|
||||
double offset[3] = {0, 0, 0};
|
||||
double euler[3] = {0, 0, 0};
|
||||
std::string separator = "";
|
||||
ReadAttr(elem, "count", 1, &count, text, true);
|
||||
ReadAttr(elem, "offset", 3, offset, text);
|
||||
ReadAttr(elem, "euler", 3, euler, text);
|
||||
ReadAttrTxt(elem, "sep", separator);
|
||||
|
||||
// store rotation difference
|
||||
mjsOrientation alt;
|
||||
mjs_defaultOrientation(alt);
|
||||
alt.type = mjORIENTATION_EULER;
|
||||
mjuu_copyvec(alt.euler, euler, 3);
|
||||
double rotation[4] = {1, 0, 0, 0};
|
||||
mjs_resolveOrientation(rotation, model->degree, model->euler, &alt);
|
||||
|
||||
// read childdef
|
||||
mjsDefault* childdef = 0;
|
||||
if (ReadAttrTxt(elem, "childclass", text)) {
|
||||
childdef = mjs_findDefault(model, text.c_str());
|
||||
mjs_findDefault(model, text.c_str());
|
||||
if (!childdef) {
|
||||
throw mjXError(elem, "unknown default childclass");
|
||||
}
|
||||
}
|
||||
|
||||
// create subtree
|
||||
mjsBody* subtree = mjs_addBody(pbody, childdef);
|
||||
double pos[3] = {0, 0, 0};
|
||||
double quat[4] = {1, 0, 0, 0};
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
// create parent frame
|
||||
mjsFrame* pframe = mjs_addFrame(subtree, frame);
|
||||
mjs_setString(pframe->info, ("line = " + std::to_string(elem->GetLineNum())).c_str());
|
||||
mjs_setDefault(pframe->element, childdef ? childdef : def);
|
||||
|
||||
// accumulate rotation
|
||||
mjuu_setvec(pframe->pos, pos[0], pos[1], pos[2]);
|
||||
mjuu_frameaccum(pos, quat, offset, rotation);
|
||||
|
||||
// overwrite orientation to increase precision
|
||||
alt.euler[0] = i*euler[0];
|
||||
alt.euler[1] = i*euler[1];
|
||||
alt.euler[2] = i*euler[2];
|
||||
mjs_resolveOrientation(quat, model->degree, model->euler, &alt);
|
||||
mjuu_setvec(pframe->quat, quat[0], quat[1], quat[2], quat[3]);
|
||||
|
||||
// process suffix
|
||||
std::string suffix = separator;
|
||||
UpdateString(suffix, count, i);
|
||||
|
||||
// process subtree
|
||||
Body(elem, subtree, pframe);
|
||||
|
||||
// attach to parent
|
||||
mjs_attachFrame(pbody, pframe, /*prefix=*/"", suffix.c_str());
|
||||
}
|
||||
|
||||
// delete subtree
|
||||
mjs_detachBody(model, subtree);
|
||||
}
|
||||
|
||||
// body sub-element
|
||||
else if (name=="body") {
|
||||
// read childdef
|
||||
|
||||
@@ -811,7 +811,7 @@ string mjXWriter::Write(char *error, size_t error_sz) {
|
||||
Extension(root);
|
||||
Custom(root);
|
||||
Asset(root);
|
||||
Body(InsertEnd(root, "worldbody"), model->GetWorld(), /*frame=*/nullptr);
|
||||
Body(InsertEnd(root, "worldbody"), model->GetWorld());
|
||||
Contact(root);
|
||||
Deformable(root);
|
||||
Equality(root);
|
||||
@@ -1456,20 +1456,31 @@ void mjXWriter::Asset(XMLElement* root) {
|
||||
|
||||
|
||||
|
||||
XMLElement* mjXWriter::OneFrame(XMLElement* elem, mjCFrame* frame) {
|
||||
if (!frame) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
if (frame->name.empty() && frame->classname.empty()) {
|
||||
return elem;
|
||||
}
|
||||
|
||||
XMLElement* frame_elem = InsertEnd(elem, "frame");
|
||||
WriteAttrTxt(frame_elem, "name", frame->name);
|
||||
WriteAttrTxt(frame_elem, "childclass", frame->classname);
|
||||
return frame_elem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// recursive body and frame writer
|
||||
void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame) {
|
||||
void mjXWriter::Body(XMLElement* elem, mjCBody* body) {
|
||||
double unitq[4] = {1, 0, 0, 0};
|
||||
|
||||
if (!body) {
|
||||
throw mjXError(0, "missing body in XML write"); // SHOULD NOT OCCUR
|
||||
}
|
||||
|
||||
// write frame if defined
|
||||
if (frame) {
|
||||
WriteAttrTxt(elem, "name", frame->name);
|
||||
WriteAttrTxt(elem, "childclass", frame->classname);
|
||||
}
|
||||
|
||||
// write body attributes and inertial
|
||||
else if (body!=model->GetWorld()) {
|
||||
WriteAttrTxt(elem, "name", body->name);
|
||||
@@ -1504,32 +1515,32 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame) {
|
||||
|
||||
// write joints
|
||||
for (int i=0; i<body->joints.size(); i++) {
|
||||
if (body->joints[i]->frame != frame) continue;
|
||||
OneJoint(InsertEnd(elem, "joint"), body->joints[i], body->joints[i]->def);
|
||||
XMLElement* celem = OneFrame(elem, body->joints[i]->frame);
|
||||
OneJoint(InsertEnd(celem, "joint"), body->joints[i], body->joints[i]->def);
|
||||
}
|
||||
|
||||
// write geoms
|
||||
for (int i=0; i<body->geoms.size(); i++) {
|
||||
if (body->geoms[i]->frame != frame) continue;
|
||||
OneGeom(InsertEnd(elem, "geom"), body->geoms[i], body->geoms[i]->def);
|
||||
XMLElement* celem = OneFrame(elem, body->geoms[i]->frame);
|
||||
OneGeom(InsertEnd(celem, "geom"), body->geoms[i], body->geoms[i]->def);
|
||||
}
|
||||
|
||||
// write sites
|
||||
for (int i=0; i<body->sites.size(); i++) {
|
||||
if (body->sites[i]->frame != frame) continue;
|
||||
OneSite(InsertEnd(elem, "site"), body->sites[i], body->sites[i]->def);
|
||||
XMLElement* celem = OneFrame(elem, body->sites[i]->frame);
|
||||
OneSite(InsertEnd(celem, "site"), body->sites[i], body->sites[i]->def);
|
||||
}
|
||||
|
||||
// write cameras
|
||||
for (int i=0; i<body->cameras.size(); i++) {
|
||||
if (body->cameras[i]->frame != frame) continue;
|
||||
OneCamera(InsertEnd(elem, "camera"), body->cameras[i], body->cameras[i]->def);
|
||||
XMLElement* celem = OneFrame(elem, body->cameras[i]->frame);
|
||||
OneCamera(InsertEnd(celem, "camera"), body->cameras[i], body->cameras[i]->def);
|
||||
}
|
||||
|
||||
// write lights
|
||||
for (int i=0; i<body->lights.size(); i++) {
|
||||
if (body->lights[i]->frame != frame) continue;
|
||||
OneLight(InsertEnd(elem, "light"), body->lights[i], body->lights[i]->def);
|
||||
XMLElement* celem = OneFrame(elem, body->lights[i]->frame);
|
||||
OneLight(InsertEnd(celem, "light"), body->lights[i], body->lights[i]->def);
|
||||
}
|
||||
|
||||
// write plugin
|
||||
@@ -1537,28 +1548,10 @@ void mjXWriter::Body(XMLElement* elem, mjCBody* body, mjCFrame* frame) {
|
||||
OnePlugin(InsertEnd(elem, "plugin"), &body->plugin);
|
||||
}
|
||||
|
||||
// write frames
|
||||
for (int i=0; i<body->frames.size(); i++) {
|
||||
// skip current frame
|
||||
if (body->frames[i]->frame != frame) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// write frame if named or has defaults
|
||||
if (!body->frames[i]->name.empty() || !body->frames[i]->classname.empty()) {
|
||||
Body(InsertEnd(elem, "frame"), body, body->frames[i]);
|
||||
}
|
||||
|
||||
// otherwise skip
|
||||
else {
|
||||
Body(elem, body, body->frames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// write child bodies recursively
|
||||
for (int i=0; i<body->bodies.size(); i++) {
|
||||
if (body->bodies[i]->frame != frame) continue;
|
||||
Body(InsertEnd(elem, "body"), body->bodies[i], nullptr);
|
||||
XMLElement* celem = OneFrame(elem, body->bodies[i]->frame);
|
||||
Body(InsertEnd(celem, "body"), body->bodies[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ class mjXWriter : public mjXBase {
|
||||
void Extension(tinyxml2::XMLElement* root); // extension section
|
||||
void Custom(tinyxml2::XMLElement* root); // custom section
|
||||
void Asset(tinyxml2::XMLElement* root); // asset section
|
||||
void Body(tinyxml2::XMLElement* elem, mjCBody* body, mjCFrame* frame); // body/world section
|
||||
void Body(tinyxml2::XMLElement* elem, mjCBody* body); // body/world section
|
||||
void Contact(tinyxml2::XMLElement* root); // contact section
|
||||
void Deformable(tinyxml2::XMLElement* root); // deformable section
|
||||
void Equality(tinyxml2::XMLElement* root); // equality section
|
||||
@@ -73,6 +73,7 @@ class mjXWriter : public mjXBase {
|
||||
void OneTendon(tinyxml2::XMLElement* elem, mjCTendon* ptendon, mjCDef* def);
|
||||
void OneActuator(tinyxml2::XMLElement* elem, mjCActuator* pactuator, mjCDef* def);
|
||||
void OnePlugin(tinyxml2::XMLElement* elem, mjsPlugin* plugin);
|
||||
tinyxml2::XMLElement* OneFrame(tinyxml2::XMLElement* elem, mjCFrame* frame);
|
||||
|
||||
bool writingdefaults; // true during defaults write
|
||||
};
|
||||
|
||||
+2
-1
@@ -344,7 +344,8 @@ bool mjXSchema::NameMatch(XMLElement* elem, int level) {
|
||||
if (name_ == "body" &&
|
||||
((level == 1 && !strcmp(elem->Value(), "worldbody")) ||
|
||||
(level != 1 && !strcmp(elem->Value(), "body")) ||
|
||||
(level >= 1 && !strcmp(elem->Value(), "frame")))) {
|
||||
(level >= 1 && !strcmp(elem->Value(), "frame")) ||
|
||||
(level >= 1 && !strcmp(elem->Value(), "replicate")))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -560,7 +560,7 @@ TEST_F(EnginePluginTest, SensorPlugin) {
|
||||
testing::ElementsAreArray<int>({3*(i+1), 6*j, 3*j}));
|
||||
EXPECT_THAT(*reinterpret_cast<mjtNum(*)[3]>(d->plugin_state +
|
||||
m->plugin_stateadr[4]),
|
||||
testing::ElementsAreArray<int>({4*(i+1), 8*j, 4*j}));
|
||||
testing::ElementsAreArray<int>({5*(i+1), 10*j, 5*j}));
|
||||
EXPECT_THAT(*reinterpret_cast<mjtNum(*)[18]>(d->sensordata),
|
||||
testing::ElementsAreArray<int>({ i+1, 2*j, j,
|
||||
5*(i+1), 10*j, 5*j,
|
||||
|
||||
+14
-8
@@ -191,7 +191,7 @@ auto Compare(T val1, T val2) {
|
||||
ReturnType magnitude = mju_abs(val1) + mju_abs(val2);
|
||||
error = mju_abs(val1/magnitude - val2/magnitude) / magnitude;
|
||||
}
|
||||
ReturnType safety_factor = 10;
|
||||
ReturnType safety_factor = 200;
|
||||
return error < safety_factor * std::numeric_limits<ReturnType>::epsilon()
|
||||
? 0
|
||||
: error;
|
||||
@@ -213,13 +213,19 @@ mjtNum CompareModel(const mjModel* m1, const mjModel* m2,
|
||||
if (maxdif > 0) return maxdif;
|
||||
|
||||
// compare arrays
|
||||
#define X(type, name, nr, nc) \
|
||||
for (int r=0; r < m1->nr; r++) \
|
||||
for (int c=0; c < nc; c++) { \
|
||||
dif = Compare(m1->name[r*nc+c], m2->name[r*nc+c]); \
|
||||
if (dif > maxdif) { maxdif = dif; field = #name;} }
|
||||
MJMODEL_POINTERS
|
||||
#undef X
|
||||
#define X(type, name, nr, nc) \
|
||||
for (int r = 0; r < m1->nr; r++) \
|
||||
for (int c = 0; c < nc; c++) { \
|
||||
dif = Compare(m1->name[r * nc + c], m2->name[r * nc + c]); \
|
||||
if (dif > maxdif) { \
|
||||
maxdif = dif; \
|
||||
field = #name; \
|
||||
field += " row: " + std::to_string(r); \
|
||||
field += " col: " + std::to_string(c); \
|
||||
} \
|
||||
} // NOLINT
|
||||
MJMODEL_POINTERS
|
||||
#undef X
|
||||
|
||||
// compare scalars in mjOption
|
||||
#define X(type, name) \
|
||||
|
||||
@@ -204,12 +204,17 @@ static constexpr char xml_child[] = R"(
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<asset>
|
||||
<texture name="texture" type="2d" builtin="checker" width="32" height="32"/>
|
||||
<material name="material" texture="texture" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<frame name="pframe">
|
||||
<frame name="cframe">
|
||||
<body name="body">
|
||||
<joint type="hinge" name="hinge"/>
|
||||
<geom class="cylinder"/>
|
||||
<geom class="cylinder" material="material"/>
|
||||
<light mode="targetbody" target="targetbody"/>
|
||||
<body name="targetbody"/>
|
||||
<body/>
|
||||
@@ -254,10 +259,15 @@ TEST_F(MujocoTest, AttachSame) {
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<asset>
|
||||
<texture name="texture" type="2d" builtin="checker" width="32" height="32"/>
|
||||
<material name="material" texture="texture" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<body name="body">
|
||||
<joint type="hinge" name="hinge"/>
|
||||
<geom class="cylinder"/>
|
||||
<geom class="cylinder" material="material"/>
|
||||
<light mode="targetbody" target="targetbody"/>
|
||||
<body name="targetbody"/>
|
||||
<body/>
|
||||
@@ -266,7 +276,7 @@ TEST_F(MujocoTest, AttachSame) {
|
||||
<frame name="frame" pos=".1 0 0" euler="0 90 0">
|
||||
<body name="attached-body-1">
|
||||
<joint type="hinge" name="attached-hinge-1"/>
|
||||
<geom class="cylinder"/>
|
||||
<geom class="cylinder" material="material"/>
|
||||
<light mode="targetbody" target="attached-targetbody-1"/>
|
||||
<body name="attached-targetbody-1"/>
|
||||
<body/>
|
||||
@@ -374,6 +384,11 @@ TEST_F(MujocoTest, AttachDifferent) {
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<asset>
|
||||
<texture name="attached-texture-1" type="2d" builtin="checker" width="32" height="32"/>
|
||||
<material name="attached-material-1" texture="attached-texture-1" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<body name="sphere">
|
||||
<freejoint/>
|
||||
@@ -381,7 +396,7 @@ TEST_F(MujocoTest, AttachDifferent) {
|
||||
<frame name="frame" pos=".1 0 0" euler="0 90 0">
|
||||
<body name="attached-body-1">
|
||||
<joint type="hinge" name="attached-hinge-1"/>
|
||||
<geom class="attached-cylinder-1"/>
|
||||
<geom class="attached-cylinder-1" material="attached-material-1"/>
|
||||
<light mode="targetbody" target="attached-targetbody-1"/>
|
||||
<body name="attached-targetbody-1"/>
|
||||
<body/>
|
||||
@@ -478,6 +493,11 @@ TEST_F(MujocoTest, AttachFrame) {
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<asset>
|
||||
<texture name="attached-texture-1" type="2d" builtin="checker" width="32" height="32"/>
|
||||
<material name="attached-material-1" texture="attached-texture-1" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<body name="sphere">
|
||||
<freejoint/>
|
||||
@@ -487,7 +507,7 @@ TEST_F(MujocoTest, AttachFrame) {
|
||||
<frame name="cframe">
|
||||
<body name="attached-body-1">
|
||||
<joint type="hinge" name="attached-hinge-1"/>
|
||||
<geom class="attached-cylinder-1"/>
|
||||
<geom class="attached-cylinder-1" material="attached-material-1"/>
|
||||
<light mode="targetbody" target="attached-targetbody-1"/>
|
||||
<body name="attached-targetbody-1"/>
|
||||
<body/>
|
||||
@@ -568,6 +588,11 @@ TEST_F(MujocoTest, DetachBody) {
|
||||
|
||||
static constexpr char xml_result[] = R"(
|
||||
<mujoco>
|
||||
<asset>
|
||||
<texture name="texture" type="2d" builtin="checker" width="32" height="32"/>
|
||||
<material name="material" texture="texture" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<frame name="pframe">
|
||||
<frame name="cframe">
|
||||
@@ -605,7 +630,6 @@ TEST_F(MujocoTest, DetachBody) {
|
||||
|
||||
// destroy everything
|
||||
mjs_deleteSpec(child);
|
||||
mjs_deleteBody(body);
|
||||
mj_deleteModel(m_detached);
|
||||
mj_deleteModel(m_expected);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <mujoco/mujoco.h>
|
||||
#include "src/cc/array_safety.h"
|
||||
#include "src/engine/engine_util_errmem.h"
|
||||
#include "src/user/user_api.h"
|
||||
#include "src/xml/xml.h"
|
||||
#include "test/fixture.h"
|
||||
|
||||
namespace mujoco {
|
||||
@@ -888,6 +890,171 @@ TEST_F(XMLReaderTest, DuplicateFrameName) {
|
||||
EXPECT_THAT(error.data(), HasSubstr("repeated name 'frame1'"));
|
||||
}
|
||||
|
||||
|
||||
// ---------------------- test replicate parsing -------------------------------
|
||||
TEST_F(XMLReaderTest, ParseReplicate) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<asset>
|
||||
<texture name="texture" type="2d" builtin="checker" width="32" height="32"/>
|
||||
<material name="material" texture="texture" texrepeat="1 1" texuniform="true"/>
|
||||
</asset>
|
||||
|
||||
<worldbody>
|
||||
<replicate count="2" offset="1 0 0">
|
||||
<replicate count="2" offset="0 1 0" sep="_">
|
||||
<geom name="geom" size="1" pos="0 0 1" material="material"/>
|
||||
<site name="site" pos="1 0 0"/>
|
||||
</replicate>
|
||||
</replicate>
|
||||
|
||||
<replicate count="101" euler="0 0 1.8">
|
||||
<geom name="g" size="1" pos="0 -1 0"/>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
|
||||
<sensor>
|
||||
<framepos name="sensor" objtype="site" objname="site"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(m, testing::NotNull()) << error.data();
|
||||
EXPECT_THAT(m->ngeom, 105);
|
||||
EXPECT_THAT(m->nsensor, 4);
|
||||
|
||||
// check that the separator is used correctly
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < 2; ++j) {
|
||||
char geom_name[mjMAXUINAME] = "";
|
||||
util::strcat_arr(geom_name, m->names + m->name_geomadr[2*i+j]);
|
||||
EXPECT_THAT(m->geom_pos[6*i+3*j+0], i);
|
||||
EXPECT_THAT(m->geom_pos[6*i+3*j+1], j);
|
||||
EXPECT_THAT(m->geom_pos[6*i+3*j+2], 1);
|
||||
EXPECT_THAT(std::string(geom_name),
|
||||
"geom_" + std::to_string(j) + std::to_string(i));
|
||||
}
|
||||
}
|
||||
|
||||
// check that 3 digits are used if count > 99
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
for (int k = 0; k < 10; ++k) {
|
||||
int ngeom = 100*i+10*j+k;
|
||||
if (ngeom > 99) break;
|
||||
char geom_name[mjMAXUINAME] = "";
|
||||
util::strcat_arr(geom_name, m->names + m->name_geomadr[4+ngeom]);
|
||||
EXPECT_THAT(
|
||||
std::string(geom_name),
|
||||
"g" + std::to_string(i) + std::to_string(j) + std::to_string(k));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check that the final pose is correct
|
||||
int n = 104;
|
||||
EXPECT_NEAR(m->geom_pos[3*n+0], 0, 1e-8);
|
||||
EXPECT_NEAR(m->geom_pos[3*n+1], 1, 1e-8);
|
||||
EXPECT_EQ(m->geom_pos[3*n+2], 0);
|
||||
EXPECT_NEAR(m->geom_quat[4*n+0], 0, 1e-8);
|
||||
EXPECT_EQ(m->geom_quat[4*n+1], 0);
|
||||
EXPECT_EQ(m->geom_quat[4*n+2], 0);
|
||||
EXPECT_EQ(m->geom_quat[4*n+3], 1);
|
||||
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, ParseReplicatePartialReference) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<worldbody>
|
||||
<geom name="floor" type="plane" size="1 1 1"/>
|
||||
<site name="world"/>
|
||||
|
||||
<replicate count="2" offset="1 0 0">
|
||||
<body name="replicated">
|
||||
<freejoint/>
|
||||
<site name="replicated"/>
|
||||
<geom name="replicated" size="1"/>
|
||||
</body>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
|
||||
<contact>
|
||||
<pair geom1="floor" geom2="replicated" condim="1"/>
|
||||
<exclude body1="world" body2="replicated" />
|
||||
</contact>
|
||||
|
||||
<tendon>
|
||||
<spatial>
|
||||
<site site="world"/>
|
||||
<site site="replicated"/>
|
||||
</spatial>
|
||||
</tendon>
|
||||
|
||||
<sensor>
|
||||
<framepos name="sensor" objtype="site" objname="replicated" reftype="site" refname="world"/>
|
||||
</sensor>
|
||||
</mujoco>
|
||||
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjModel* m = LoadModelFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(m, testing::NotNull()) << error.data();
|
||||
|
||||
EXPECT_THAT(m->nbody, 3);
|
||||
EXPECT_THAT(m->ngeom, 3);
|
||||
EXPECT_THAT(m->npair, 2);
|
||||
EXPECT_THAT(m->nexclude, 2);
|
||||
EXPECT_THAT(m->ntendon, 2);
|
||||
EXPECT_THAT(m->nsensor, 2);
|
||||
|
||||
mj_deleteModel(m);
|
||||
}
|
||||
|
||||
TEST_F(XMLReaderTest, ParseReplicateDefaultPropagate) {
|
||||
static constexpr char xml[] = R"(
|
||||
<mujoco>
|
||||
<default>
|
||||
<default class="body">
|
||||
<geom type="capsule"/>
|
||||
</default>
|
||||
</default>
|
||||
|
||||
<worldbody>
|
||||
<replicate count="2" euler="0 0 16.36" sep="-">
|
||||
<body name="torso" pos="-5 0 1.282" childclass="body">
|
||||
<body name="head" pos="0 0 .19">
|
||||
<geom name="head" type="sphere" size=".09"/>
|
||||
</body>
|
||||
</body>
|
||||
</replicate>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
)";
|
||||
std::array<char, 1024> error;
|
||||
mjSpec* spec = ParseSpecFromString(xml, error.data(), error.size());
|
||||
EXPECT_THAT(spec, NotNull()) << error.data();
|
||||
|
||||
mjsBody* torso = mjs_findBody(spec, "torso-0");
|
||||
EXPECT_THAT(torso, NotNull());
|
||||
|
||||
mjsDefault* def = mjs_getDefault(torso->element);
|
||||
EXPECT_THAT(def, NotNull());
|
||||
EXPECT_THAT(def->geom->type, mjGEOM_CAPSULE);
|
||||
|
||||
mjsBody* head = mjs_findBody(spec, "head-0");
|
||||
EXPECT_THAT(head, NotNull());
|
||||
|
||||
def = mjs_getDefault(head->element);
|
||||
EXPECT_THAT(def, NotNull());
|
||||
EXPECT_THAT(def->geom->type, mjGEOM_CAPSULE);
|
||||
|
||||
mjs_deleteSpec(spec);
|
||||
}
|
||||
|
||||
// ----------------------- test camera parsing ---------------------------------
|
||||
|
||||
TEST_F(XMLReaderTest, CameraInvalidFovyAndSensorsize) {
|
||||
|
||||
Reference in New Issue
Block a user