Add "frame" attribute to <attach> element.
The `<attach>` element now supports a "frame" attribute, allowing users to specify either a "body" or a "frame" to attach to, but not both. The XML parser has been updated to handle this new attribute and the mutual exclusivity constraint. PiperOrigin-RevId: 941245904 Change-Id: I4c0edeed5f3a9e456aed96c373b1647fe79841d9
This commit is contained in:
committed by
Copybara-Service
parent
1f6cf4035c
commit
c6c3ec3149
+29
-12
@@ -3996,33 +3996,50 @@ Associate this body with an :ref:`engine plugin<exPlugin>`. Either :at:`plugin`
|
||||
:el-prefix:`body/` |-| **attach** |*|
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The :el:`attach` element is used to insert a sub-tree of bodies from another model into this model's kinematic tree.
|
||||
The :el:`attach` element is used to insert elements from another (child) model into this (parent) model's kinematic tree.
|
||||
Unlike :ref:`include<include>`, which is implemented in the parser and is equivalent to copying and pasting XML from one
|
||||
file into another, :el:`attach` is implemented in the model compiler. In order to use this element, the sub-model must
|
||||
first be defined as an :ref:`asset<asset-model>`. When creating an attachment, the top body of the attached subtree is
|
||||
specified, and all referencing elements outside the kinematic tree (e.g., sensors and actuators), are also copied into
|
||||
the top-level model. Additionally, any elements referenced from within the attached subtree (e.g. defaults and assets)
|
||||
will be copied in to the top-level model. :el:`attach` is a :ref:`meta-element`, so upon saving all attachments will
|
||||
first be defined as an :ref:`asset<asset-model>`. When creating an attachment, a frame, body or the entire child model in the
|
||||
child model is specified, and all referencing elements outside the kinematic tree (e.g., sensors and actuators), are also copied into
|
||||
the parent model. Additionally, any elements referenced from within the attached subtree (e.g. defaults and assets)
|
||||
will be copied in to the parent model. :el:`attach` is a :ref:`meta-element`, so upon saving all attachments will
|
||||
appear in the saved XML file. Note that this element is a subset of the functionality of the procedural
|
||||
:ref:`attachment<meAttachment>` functionality. As such, it shares the same limitations as described there. In addition,
|
||||
when the :el:`attach` element is used, it is not possible to attach an entire model (i.e. including all elements,
|
||||
referenced or not).
|
||||
:ref:`attachment<meAttachment>` functionality. As such, it shares the same limitations as described there. See example `here
|
||||
<https://github.com/google-deepmind/mujoco/blob/main/test/xml/testdata/parent.xml>`__.
|
||||
|
||||
.. admonition:: Known issues
|
||||
:class: note
|
||||
|
||||
The following known limitations exist, to be addressed in a future release:
|
||||
|
||||
- All assets from the child model will be copied in, whether they are referenced or not.
|
||||
- Circular references are not checked for and will lead to infinite loops.
|
||||
- When attaching a model with :ref:`keyframes<keyframe>`, model compilation is required for the re-indexing to be
|
||||
finalized. If a second attachment is performed without compilation, the keyframes from the first attachment will be
|
||||
lost.
|
||||
|
||||
.. _body-attach-model:
|
||||
|
||||
:at:`model`: :at-val:`string, required`
|
||||
The sub-model from which to attach a subtree.
|
||||
The child model from which to attach a subtree or a frame.
|
||||
|
||||
.. _body-attach-body:
|
||||
|
||||
:at:`body`: :at-val:`string, optional`
|
||||
Name of the body in the sub-model to attach here. The body and its subtree will be attached. If this attribute is not
|
||||
specified, the contents of the world body will be attached in a new :ref:`frame<body-frame>`.
|
||||
Name of the body in the child model to attach here. The body and its subtree will be attached. If neither this
|
||||
attribute nor :ref:`frame<body-attach-frame>` is specified (only one allowed), the contents of the world body will
|
||||
be attached in a new :ref:`frame<body-frame>`.
|
||||
|
||||
.. _body-attach-frame:
|
||||
|
||||
:at:`frame`: :at-val:`string, optional`
|
||||
Name of the frame in the child model to attach here. If neither this attribute nor :ref:`body<body-attach-body>` is
|
||||
specified (only one allowed), the contents of the world body will be attached in a new :ref:`frame<body-frame>`.
|
||||
|
||||
.. _body-attach-prefix:
|
||||
|
||||
:at:`prefix`: :at-val:`string, required`
|
||||
Prefix to prepend to names of elements in the sub-model. This attribute is required to prevent name collisions with
|
||||
Prefix to prepend to names of elements in the child model. This attribute is required to prevent name collisions with
|
||||
the parent or when attaching the same sub-tree multiple times.
|
||||
|
||||
|
||||
|
||||
Executable → Regular
+3
@@ -983,6 +983,9 @@
|
||||
.. grid-item::
|
||||
:ref:`body<body-attach-body>`
|
||||
|
||||
.. grid-item::
|
||||
:ref:`frame<body-attach-frame>`
|
||||
|
||||
.. grid-item::
|
||||
:ref:`prefix<body-attach-prefix>`
|
||||
|
||||
|
||||
@@ -470,7 +470,7 @@ std::vector<const char*> MJCF[nMJCF] = {
|
||||
{"config", "*", "key", "value"},
|
||||
{">"},
|
||||
{">"},
|
||||
{"attach", "*", "model", "body", "prefix"},
|
||||
{"attach", "*", "model", "body", "frame", "prefix"},
|
||||
{"site", "*", "name", "class", "type", "group", "pos", "quat", "material",
|
||||
"size", "fromto", "axisangle", "xyaxes", "zaxis", "euler", "rgba", "user"},
|
||||
{"camera", "*", "name", "class", "projection",
|
||||
@@ -4170,35 +4170,40 @@ void mjXReader::Body(XMLElement* section, mjsBody* body, mjsFrame* frame,
|
||||
|
||||
// attachment
|
||||
else if (name == "attach") {
|
||||
string model_name, body_name, prefix;
|
||||
string model_name, child_name, prefix;
|
||||
ReadAttrTxt(elem, "model", model_name, /*required=*/true);
|
||||
ReadAttrTxt(elem, "body", body_name, /*required=*/false);
|
||||
bool has_body = ReadAttrTxt(elem, "body", child_name, /*required=*/false);
|
||||
bool has_frame = ReadAttrTxt(elem, "frame", child_name, /*required=*/false);
|
||||
ReadAttrTxt(elem, "prefix", prefix, /*required=*/true);
|
||||
|
||||
mjsBody* child_body = mjs_findBody(spec, (prefix+body_name).c_str());
|
||||
if (has_body && has_frame) {
|
||||
throw mjXError(elem, "only one of body or frame can be specified in attach");
|
||||
}
|
||||
mjtObj type = has_body ? mjOBJ_BODY : mjOBJ_FRAME;
|
||||
|
||||
mjsElement* child = mjs_findElement(spec, type, (prefix+child_name).c_str());
|
||||
mjsFrame* pframe = frame ? frame : mjs_addFrame(body, nullptr);
|
||||
|
||||
if (!child_body) {
|
||||
if (!child) {
|
||||
mjSpec* asset = mjs_findSpec(spec, model_name.c_str());
|
||||
if (!asset) {
|
||||
throw mjXError(elem, "could not find model '%s'", model_name.c_str());
|
||||
}
|
||||
mjsElement* child;
|
||||
if (body_name.empty()) {
|
||||
if (child_name.empty()) {
|
||||
child = asset->element;
|
||||
} else {
|
||||
child_body = mjs_findBody(asset, body_name.c_str());
|
||||
if (!child_body) {
|
||||
throw mjXError(elem, "could not find body '%s''%s'", body_name.c_str());
|
||||
child = mjs_findElement(asset, type, child_name.c_str());
|
||||
if (!child) {
|
||||
throw mjXError(elem, "could not find %s",
|
||||
(string(mju_type2Str(type)) + " '" + child_name + "'").c_str());
|
||||
}
|
||||
child = child_body->element;
|
||||
}
|
||||
if (!mjs_attach(pframe->element, child, prefix.c_str(), "")) {
|
||||
throw mjXError(elem, "%s", stripError(mjs_getError(spec)));
|
||||
}
|
||||
} else {
|
||||
// only set frame to existing body
|
||||
if (mjs_setFrame(child_body->element, pframe)) {
|
||||
if (mjs_setFrame(child, pframe)) {
|
||||
throw mjXError(elem, "%s", stripError(mjs_getError(spec)));
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+12
-1
@@ -1,7 +1,18 @@
|
||||
<mujoco model="child">
|
||||
<worldbody>
|
||||
<body name="body">
|
||||
<geom name="geom" size="1" pos="2 0 0"/>
|
||||
<geom name="geom" size="1" pos="0 0 -2" rgba="1 0 0 1"/>
|
||||
</body>
|
||||
|
||||
<frame name="frame">
|
||||
<body name="body2" pos="-1 0 0">
|
||||
<freejoint/>
|
||||
<geom name="geom2" type="box" size="1 1 1" rgba="0 1 0 1"/>
|
||||
</body>
|
||||
<body name="body3" pos="1 0 0">
|
||||
<freejoint/>
|
||||
<geom name="geom3" type="box" size="1 1 1" rgba="0 1 0 1"/>
|
||||
</body>
|
||||
</frame>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
Vendored
+16
-2
@@ -3,9 +3,23 @@
|
||||
<model name="other" file="child.xml"/>
|
||||
</asset>
|
||||
<worldbody>
|
||||
<!-- attach body to body -->
|
||||
<body name="parent">
|
||||
<geom name="geom" size="2"/>
|
||||
<attach model="other" body="body" prefix="other"/>
|
||||
<geom name="geom" size="1" pos="0 0 2"/>
|
||||
<attach model="other" body="body" prefix="other1"/>
|
||||
</body>
|
||||
|
||||
<!-- attach frame to body (world) -->
|
||||
<attach model="other" frame="frame" prefix="other2"/>
|
||||
|
||||
<!-- attach frame to frame -->
|
||||
<frame name="top" pos="0 0 4">
|
||||
<attach model="other" frame="frame" prefix="other3"/>
|
||||
</frame>
|
||||
|
||||
<!-- attach body to frame -->
|
||||
<frame name="bottom-right" pos="2 0 -1">
|
||||
<attach model="other" body="body" prefix="other4"/>
|
||||
</frame>
|
||||
</worldbody>
|
||||
</mujoco>
|
||||
|
||||
Reference in New Issue
Block a user