Skip to content

Commit 757251e

Browse files
authored
Merge branch 'google-deepmind:main' into musculoskeletal_dog_creation
2 parents c36590f + b72eaa4 commit 757251e

11 files changed

Lines changed: 335 additions & 293 deletions

File tree

dm_control/mjcf/README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ collision occurs.
2222
```python
2323
from dm_control import mjcf
2424

25-
class Arm(object):
25+
class Arm:
2626

2727
def __init__(self, name):
2828
self.mjcf_model = mjcf.RootElement(model=name)
@@ -38,7 +38,7 @@ class Arm(object):
3838
self.forearm.add('geom', name='forearm', type='capsule',
3939
pos=[0, 0, -0.15], size=[0.045, 0.15])
4040

41-
class UpperBody(object):
41+
class UpperBody:
4242

4343
def __init__(self):
4444
self.mjcf_model = mjcf.RootElement()
@@ -430,10 +430,10 @@ ensure that two models do not become subtly "incompatible". For example:
430430

431431
```python
432432
model_1 = mjcf.RootElement()
433-
model_1.compiler.angle = 'radians'
433+
model_1.compiler.angle = 'radian'
434434

435435
model_2 = mjcf.RootElement()
436-
model_2.compiler.angle = 'degrees'
436+
model_2.compiler.angle = 'degree'
437437

438438
model_1.attach(model_2) # Error!
439439
```
@@ -446,14 +446,14 @@ become problematic is:
446446
model_1 = mjcf.RootElement()
447447

448448
model_2 = mjcf.RootElement()
449-
model_2.compiler.angle = 'degrees'
449+
model_2.compiler.angle = 'degree'
450450

451451
model_1.attach(model_2) # No error, but all angles in model_1 are now wrong!
452452
```
453453

454454
Here, `model_1` assumes MuJoCo's default angle unit of radians. Since it does
455455
not explicitly assign a value to `compiler.angle`, PyMJCF does not detect a
456-
conflict with `angle=degrees` in `model_2`. All angles in `model_1` are now
456+
conflict with `angle=degree` in `model_2`. All angles in `model_1` are now
457457
incorrectly interpreted as degrees.
458458

459459
### Elements outside of `<worldbody>`

dm_control/mjcf/attribute.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -511,24 +511,41 @@ def _get_asset_from_path(self, path):
511511
_, basename = os.path.split(path)
512512
filename, extension = os.path.splitext(basename)
513513

514-
# Look in the dict of pre-loaded assets before checking the filesystem.
515-
try:
514+
assetdir = None
515+
if self._parent.namescope.has_identifier(
516+
constants.BASEPATH, constants.ASSETDIR_NAMESPACE
517+
):
518+
assetdir = self._parent.namescope.get(
519+
constants.BASEPATH, constants.ASSETDIR_NAMESPACE
520+
)
521+
522+
if path in self._parent.namescope.assets:
523+
# Look in the dict of pre-loaded assets before checking the filesystem.
516524
contents = self._parent.namescope.assets[path]
517-
except KeyError:
525+
else:
518526
# Construct the full path to the asset file, prefixed by the path to the
519527
# model directory, and by `meshdir` or `texturedir` if appropriate.
520528
path_parts = []
521529
if self._parent.namescope.model_dir:
522530
path_parts.append(self._parent.namescope.model_dir)
523-
try:
524-
base_path = self._parent.namescope.get(constants.BASEPATH,
525-
self._path_namespace)
531+
532+
if self._parent.namescope.has_identifier(
533+
constants.BASEPATH, self._path_namespace
534+
):
535+
base_path = self._parent.namescope.get(
536+
constants.BASEPATH, self._path_namespace
537+
)
526538
path_parts.append(base_path)
527-
except KeyError:
528-
pass
539+
elif (
540+
self._path_namespace
541+
in (constants.TEXTUREDIR_NAMESPACE, constants.MESHDIR_NAMESPACE)
542+
and assetdir is not None
543+
):
544+
path_parts.append(assetdir)
529545
path_parts.append(path)
530546
full_path = os.path.join(*path_parts) # pylint: disable=no-value-for-parameter
531547
contents = resources.GetResource(full_path)
548+
532549
if self._parent.tag == constants.SKIN:
533550
return SkinAsset(contents=contents, parent=self._parent,
534551
extension=extension, prefix=filename)

dm_control/mjcf/constants.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@
3939
TENDON = 'tendon'
4040
WORLDBODY = 'worldbody'
4141

42+
# Path namespaces.
43+
MESHDIR_NAMESPACE = 'mesh'
44+
TEXTUREDIR_NAMESPACE = 'texture'
45+
ASSETDIR_NAMESPACE = 'asset'
46+
4247
MJDATA_TRIGGERS_DIRTY = [
4348
'qpos', 'qvel', 'act', 'ctrl', 'qfrc_applied', 'xfrc_applied']
4449
MJMODEL_DOESNT_TRIGGER_DIRTY = [
@@ -63,9 +68,7 @@
6368
'body_iquat': ('simple', 'sameframe'),
6469
}
6570

66-
# This is the actual upper limit on VFS filename length, despite what it says
67-
# in the header file (100) or the error message (99).
68-
MAX_VFS_FILENAME_LENGTH = 98
71+
MAX_VFS_FILENAME_LENGTH = 998
6972

7073
# The prefix used in the schema to denote reference_namespace that are defined
7174
# via another attribute.

dm_control/mjcf/export_with_assets_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
_ASSETS_DIR = os.path.join(os.path.dirname(__file__), 'test_assets')
2929
_TEST_MODEL_WITH_ASSETS = os.path.join(_ASSETS_DIR, 'model_with_assets.xml')
30+
_TEST_MODEL_WITH_ASSETDIR = os.path.join(_ASSETS_DIR, 'model_with_assetdir.xml')
3031
_TEST_MODEL_WITHOUT_ASSETS = os.path.join(_ASSETS_DIR, 'lego_brick.xml')
3132

3233

@@ -42,6 +43,7 @@ class ExportWithAssetsTest(parameterized.TestCase):
4243

4344
@parameterized.named_parameters(
4445
('with_assets', _TEST_MODEL_WITH_ASSETS, 'mujoco_with_assets.xml'),
46+
('with_assetdir', _TEST_MODEL_WITH_ASSETDIR, 'mujoco_with_assetdir.xml'),
4547
('without_assets', _TEST_MODEL_WITHOUT_ASSETS, 'mujoco.xml'),)
4648
def test_export_model(self, xml_path, out_xml_name):
4749
"""Save processed MJCF model."""

dm_control/mjcf/parser.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from dm_control.mjcf import debugging
2323
from dm_control.mjcf import element
2424
from lxml import etree
25+
# Copybara placeholder for internal file handling dependency.
2526
from dm_control.utils import io as resources
2627

2728

@@ -213,7 +214,7 @@ def _parse_children(xml_element, mjcf_element, escape_separators=False):
213214
mjcf_child.set_attributes(**attributes)
214215
except: # pylint: disable=bare-except
215216
err_type, err, traceback = sys.exc_info()
216-
raise err_type(
217+
raise err_type( # pylint: disable=raise-missing-from
217218
f'Line {xml_child.sourceline}: error while parsing element '
218219
f'<{xml_child.tag}>: {err}').with_traceback(traceback)
219220
_parse_children(xml_child, mjcf_child, escape_separators)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<mujoco model="Textured cube and mesh">
2+
<compiler assetdir="meshes" texturedir="textures"/>
3+
<asset>
4+
<mesh name="cube" file="cube.stl"/>
5+
<mesh name="another_cube" file="more_meshes/cube.stl"/>
6+
<mesh name="unused_asset_should_not_cause_problems" file="cube.stl"/>
7+
<mesh name="cube_msh" file="cube.msh"/>
8+
<texture name="texture" file="deepmind.png"/>
9+
<material name="mat_texture" texture="texture"/>
10+
<hfield name="hill" file="../textures/deepmind.png" size="0.5 0.5 0.5 0.1"/>
11+
</asset>
12+
<worldbody>
13+
<light diffuse=".5 .5 .5" pos="0 0 3" dir="0 0 -1"/>
14+
<geom type="mesh" mesh="cube" material="mat_texture"/>
15+
<geom type="mesh" mesh="another_cube" material="mat_texture" pos="2.5 0. 0."/>
16+
<geom type="mesh" mesh="cube_msh" material="mat_texture" pos="4. 0. 0."/>
17+
<geom type="hfield" hfield="hill" pos="1.2 0. 0" rgba="0. 0.9 0. 1" size="40 40 0.1"/>
18+
</worldbody>
19+
</mujoco>

0 commit comments

Comments
 (0)