Merge branch 'google-deepmind:main' into usd-integration
This commit is contained in:
@@ -84,7 +84,7 @@ if(NOT TARGET mujoco)
|
||||
if(MUJOCO_FRAMEWORK)
|
||||
message("MuJoCo framework is at ${MUJOCO_FRAMEWORK}/mujoco.framework")
|
||||
set(MUJOCO_LIBRARY
|
||||
${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.0.2.dylib
|
||||
${MUJOCO_FRAMEWORK}/mujoco.framework/Versions/A/libmujoco.3.1.2.dylib
|
||||
)
|
||||
target_compile_options(mujoco INTERFACE -F${MUJOCO_FRAMEWORK})
|
||||
endif()
|
||||
@@ -92,7 +92,7 @@ if(NOT TARGET mujoco)
|
||||
|
||||
if(NOT MUJOCO_FRAMEWORK)
|
||||
find_library(
|
||||
MUJOCO_LIBRARY mujoco mujoco.3.0.2 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED
|
||||
MUJOCO_LIBRARY mujoco mujoco.3.1.2 HINTS ${MUJOCO_LIBRARY_DIR} REQUIRED
|
||||
)
|
||||
find_path(MUJOCO_INCLUDE mujoco/mujoco.h HINTS ${MUJOCO_INCLUDE_DIR} REQUIRED)
|
||||
message("MuJoCo is at ${MUJOCO_LIBRARY}")
|
||||
@@ -173,16 +173,17 @@ findorfetch(
|
||||
GIT_REPO
|
||||
https://gitlab.com/libeigen/eigen
|
||||
GIT_TAG
|
||||
aa6964bf3a34fd607837dd8123bc42465185c4f8
|
||||
454f89af9d6f3525b1df5f9ef9c86df58bf2d4d3
|
||||
TARGETS
|
||||
Eigen3::Eigen
|
||||
EXCLUDE_FROM_ALL
|
||||
)
|
||||
|
||||
# ==================== PYBIND11 ================================================
|
||||
option(MUJOCO_PYTHON_USE_SYSTEM_PYBIND11 "Use installed pybind11 version." OFF)
|
||||
findorfetch(
|
||||
USE_SYSTEM_PACKAGE
|
||||
OFF
|
||||
MUJOCO_PYTHON_USE_SYSTEM_PYBIND11
|
||||
PACKAGE_NAME
|
||||
pybind11
|
||||
LIBRARY_NAME
|
||||
|
||||
@@ -822,7 +822,7 @@ Euler integrator, semi-implicit in velocity.
|
||||
self.assertLen(mujoco.mjRNDSTRING, mujoco.mjtRndFlag.mjNRNDFLAG)
|
||||
self.assertEqual(mujoco.mjDISABLESTRING[11], 'Refsafe')
|
||||
self.assertEqual(mujoco.mjVISSTRING[mujoco.mjtVisFlag.mjVIS_INERTIA],
|
||||
('&Inertia', '0', 'I'))
|
||||
('Inertia', '0', 'I'))
|
||||
|
||||
def test_enum_values(self):
|
||||
self.assertEqual(mujoco.mjtJoint.mjJNT_FREE, 0)
|
||||
@@ -840,7 +840,7 @@ Euler integrator, semi-implicit in velocity.
|
||||
self.assertEqual(mujoco.mjtGeom.mjGEOM_ARROW, 100)
|
||||
self.assertEqual(mujoco.mjtGeom.mjGEOM_ARROW1, 101)
|
||||
self.assertEqual(mujoco.mjtGeom.mjGEOM_ARROW2, 102)
|
||||
self.assertEqual(mujoco.mjtGeom.mjGEOM_TRIANGLE, 107)
|
||||
self.assertEqual(mujoco.mjtGeom.mjGEOM_TRIANGLE, 108)
|
||||
self.assertEqual(mujoco.mjtGeom.mjGEOM_NONE, 1001)
|
||||
|
||||
def test_enum_from_int(self):
|
||||
|
||||
@@ -57,15 +57,18 @@ class GLContext:
|
||||
|
||||
def free(self):
|
||||
"""Frees resources associated with this context."""
|
||||
if self._context:
|
||||
cgl.CGLUnlockContext(self._context)
|
||||
cgl.CGLSetCurrentContext(None)
|
||||
cgl.CGLReleaseContext(self._context)
|
||||
self._context = None
|
||||
try:
|
||||
if self._context:
|
||||
cgl.CGLUnlockContext(self._context)
|
||||
cgl.CGLSetCurrentContext(None)
|
||||
cgl.CGLReleaseContext(self._context)
|
||||
self._context = None
|
||||
|
||||
if self._pix:
|
||||
cgl.CGLReleasePixelFormat(self._pix)
|
||||
self._context = None
|
||||
if self._pix:
|
||||
cgl.CGLReleasePixelFormat(self._pix)
|
||||
self._pix = None
|
||||
except Exception: # pylint: disable=broad-exception-caught
|
||||
pass
|
||||
|
||||
def __del__(self):
|
||||
self.free()
|
||||
|
||||
@@ -92,12 +92,13 @@ class GLContext:
|
||||
del max_width, max_height # unused
|
||||
num_configs = ctypes.c_long()
|
||||
config_size = 1
|
||||
config = EGL.EGLConfig()
|
||||
# ctypes syntax for making an array of length config_size.
|
||||
configs = (EGL.EGLConfig * config_size)()
|
||||
EGL.eglReleaseThread()
|
||||
EGL.eglChooseConfig(
|
||||
EGL_DISPLAY,
|
||||
EGL_ATTRIBUTES,
|
||||
ctypes.byref(config),
|
||||
configs,
|
||||
config_size,
|
||||
num_configs)
|
||||
if num_configs.value < 1:
|
||||
@@ -106,7 +107,7 @@ class GLContext:
|
||||
'desired attributes: {}'.format(EGL_ATTRIBUTES))
|
||||
EGL.eglBindAPI(EGL.EGL_OPENGL_API)
|
||||
self._context = EGL.eglCreateContext(
|
||||
EGL_DISPLAY, config, EGL.EGL_NO_CONTEXT, None)
|
||||
EGL_DISPLAY, configs[0], EGL.EGL_NO_CONTEXT, None)
|
||||
if not self._context:
|
||||
raise RuntimeError('Cannot create an EGL context.')
|
||||
|
||||
|
||||
@@ -185,6 +185,7 @@ PYBIND11_MODULE(_functions, pymodule) {
|
||||
Def<traits::mj_fwdConstraint>(pymodule);
|
||||
Def<traits::mj_Euler>(pymodule);
|
||||
Def<traits::mj_RungeKutta>(pymodule);
|
||||
Def<traits::mj_implicit>(pymodule);
|
||||
Def<traits::mj_invPosition>(pymodule);
|
||||
Def<traits::mj_invVelocity>(pymodule);
|
||||
Def<traits::mj_invConstraint>(pymodule);
|
||||
|
||||
@@ -35,7 +35,7 @@ class GLContext:
|
||||
if glfw.get_current_context() == self._context:
|
||||
glfw.make_context_current(None)
|
||||
glfw.destroy_window(self._context)
|
||||
self._context = None
|
||||
self._context = None
|
||||
|
||||
def __del__(self):
|
||||
self.free()
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.mujoco.mjpython</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>3.0.2</string>
|
||||
<string>3.1.2</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>3.0.2</string>
|
||||
<string>3.1.2</string>
|
||||
<key>CFBundleLongVersionString</key>
|
||||
<string>3.0.2</string>
|
||||
<string>3.1.2</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.0.2</string>
|
||||
<string>3.1.2</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>mjpython</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
|
||||
@@ -136,6 +136,9 @@ the clause:
|
||||
A new numpy array holding the pixels with shape `(H, W)` or `(H, W, 3)`,
|
||||
depending on the value of `self._depth_rendering` unless
|
||||
`out is None`, in which case a reference to `out` is returned.
|
||||
|
||||
Raises:
|
||||
RuntimeError: if this method is called after the close method.
|
||||
"""
|
||||
original_flags = self._scene.flags.copy()
|
||||
|
||||
@@ -145,6 +148,8 @@ the clause:
|
||||
self._scene.flags[_enums.mjtRndFlag.mjRND_SEGMENT] = True
|
||||
self._scene.flags[_enums.mjtRndFlag.mjRND_IDCOLOR] = True
|
||||
|
||||
if self._gl_context is None:
|
||||
raise RuntimeError('render cannot be called after close.')
|
||||
self._gl_context.make_current()
|
||||
|
||||
if self._depth_rendering:
|
||||
@@ -288,3 +293,38 @@ the clause:
|
||||
camera, _enums.mjtCatBit.mjCAT_ALL.value,
|
||||
self._scene,
|
||||
)
|
||||
|
||||
def close(self) -> None:
|
||||
"""Frees the resources used by the renderer.
|
||||
|
||||
This method can be used directly:
|
||||
|
||||
```python
|
||||
renderer = Renderer(...)
|
||||
# Use renderer.
|
||||
renderer.close()
|
||||
```
|
||||
|
||||
or via a context manager:
|
||||
|
||||
```python
|
||||
with Renderer(...) as renderer:
|
||||
# Use renderer.
|
||||
```
|
||||
"""
|
||||
if self._gl_context:
|
||||
self._gl_context.free()
|
||||
self._gl_context = None
|
||||
if self._mjr_context:
|
||||
self._mjr_context.free()
|
||||
self._mjr_context = None
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
del exc_type, exc_value, traceback # Unused.
|
||||
self.close()
|
||||
|
||||
def __del__(self) -> None:
|
||||
self.close()
|
||||
|
||||
@@ -33,10 +33,10 @@ class MuJoCoRendererTest(parameterized.TestCase):
|
||||
"""
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
renderer = mujoco.Renderer(model, 50, 50)
|
||||
mujoco.mj_forward(model, data)
|
||||
with self.assertRaisesRegex(ValueError, r'camera "b" does not exist'):
|
||||
renderer.update_scene(data, 'b')
|
||||
with mujoco.Renderer(model, 50, 50) as renderer:
|
||||
mujoco.mj_forward(model, data)
|
||||
with self.assertRaisesRegex(ValueError, r'camera "b" does not exist'):
|
||||
renderer.update_scene(data, 'b')
|
||||
|
||||
def test_renderer_camera_under_range(self):
|
||||
xml = """
|
||||
@@ -48,10 +48,10 @@ class MuJoCoRendererTest(parameterized.TestCase):
|
||||
"""
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
renderer = mujoco.Renderer(model, 50, 50)
|
||||
mujoco.mj_forward(model, data)
|
||||
with self.assertRaisesRegex(ValueError, '-2 is out of range'):
|
||||
renderer.update_scene(data, -2)
|
||||
with mujoco.Renderer(model, 50, 50) as renderer:
|
||||
mujoco.mj_forward(model, data)
|
||||
with self.assertRaisesRegex(ValueError, '-2 is out of range'):
|
||||
renderer.update_scene(data, -2)
|
||||
|
||||
def test_renderer_camera_over_range(self):
|
||||
xml = """
|
||||
@@ -63,10 +63,10 @@ class MuJoCoRendererTest(parameterized.TestCase):
|
||||
"""
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
renderer = mujoco.Renderer(model, 50, 50)
|
||||
mujoco.mj_forward(model, data)
|
||||
with self.assertRaisesRegex(ValueError, '1 is out of range'):
|
||||
renderer.update_scene(data, 1)
|
||||
with mujoco.Renderer(model, 50, 50) as renderer:
|
||||
mujoco.mj_forward(model, data)
|
||||
with self.assertRaisesRegex(ValueError, '1 is out of range'):
|
||||
renderer.update_scene(data, 1)
|
||||
|
||||
def test_renderer_renders_scene(self):
|
||||
xml = """
|
||||
@@ -79,19 +79,19 @@ class MuJoCoRendererTest(parameterized.TestCase):
|
||||
"""
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
renderer = mujoco.Renderer(model, 50, 50)
|
||||
mujoco.mj_forward(model, data)
|
||||
renderer.update_scene(data, 'closeup')
|
||||
with mujoco.Renderer(model, 50, 50) as renderer:
|
||||
mujoco.mj_forward(model, data)
|
||||
renderer.update_scene(data, 'closeup')
|
||||
|
||||
pixels = renderer.render().flatten()
|
||||
not_all_black = False
|
||||
pixels = renderer.render().flatten()
|
||||
not_all_black = False
|
||||
|
||||
# Pixels should all be a neutral color.
|
||||
for pixel in pixels:
|
||||
if pixel > 0:
|
||||
not_all_black = True
|
||||
break
|
||||
self.assertTrue(not_all_black)
|
||||
# Pixels should all be a neutral color.
|
||||
for pixel in pixels:
|
||||
if pixel > 0:
|
||||
not_all_black = True
|
||||
break
|
||||
self.assertTrue(not_all_black)
|
||||
|
||||
def test_renderer_output_without_out(self):
|
||||
xml = """
|
||||
@@ -105,25 +105,25 @@ class MuJoCoRendererTest(parameterized.TestCase):
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
mujoco.mj_forward(model, data)
|
||||
renderer = mujoco.Renderer(model, 50, 50)
|
||||
renderer.update_scene(data, 'closeup')
|
||||
pixels = [renderer.render()]
|
||||
|
||||
colors = (
|
||||
(1.0, 0.0, 0.0, 1.0),
|
||||
(0.0, 1.0, 0.0, 1.0),
|
||||
(0.0, 0.0, 1.0, 1.0),
|
||||
)
|
||||
|
||||
for i, color in enumerate(colors):
|
||||
model.geom_rgba[0, :] = color
|
||||
mujoco.mj_forward(model, data)
|
||||
with mujoco.Renderer(model, 50, 50) as renderer:
|
||||
renderer.update_scene(data, 'closeup')
|
||||
pixels.append(renderer.render())
|
||||
self.assertIsNot(pixels[-2], pixels[-1])
|
||||
pixels = [renderer.render()]
|
||||
|
||||
# Pixels should change over steps.
|
||||
self.assertFalse((pixels[i + 1] == pixels[i]).all())
|
||||
colors = (
|
||||
(1.0, 0.0, 0.0, 1.0),
|
||||
(0.0, 1.0, 0.0, 1.0),
|
||||
(0.0, 0.0, 1.0, 1.0),
|
||||
)
|
||||
|
||||
for i, color in enumerate(colors):
|
||||
model.geom_rgba[0, :] = color
|
||||
mujoco.mj_forward(model, data)
|
||||
renderer.update_scene(data, 'closeup')
|
||||
pixels.append(renderer.render())
|
||||
self.assertIsNot(pixels[-2], pixels[-1])
|
||||
|
||||
# Pixels should change over steps.
|
||||
self.assertFalse((pixels[i + 1] == pixels[i]).all())
|
||||
|
||||
def test_renderer_output_with_out(self):
|
||||
xml = """
|
||||
@@ -139,23 +139,21 @@ class MuJoCoRendererTest(parameterized.TestCase):
|
||||
model = mujoco.MjModel.from_xml_string(xml)
|
||||
data = mujoco.MjData(model)
|
||||
mujoco.mj_forward(model, data)
|
||||
renderer = mujoco.Renderer(model, *render_size)
|
||||
renderer.update_scene(data, 'closeup')
|
||||
with mujoco.Renderer(model, *render_size) as renderer:
|
||||
renderer.update_scene(data, 'closeup')
|
||||
|
||||
self.assertTrue(np.all(render_out == 0))
|
||||
self.assertTrue(np.all(render_out == 0))
|
||||
|
||||
pixels = renderer.render(out=render_out)
|
||||
pixels = renderer.render(out=render_out)
|
||||
|
||||
# Pixels should always refer to the same `render_out` array.
|
||||
self.assertIs(pixels, render_out)
|
||||
self.assertFalse(np.all(render_out == 0))
|
||||
# Pixels should always refer to the same `render_out` array.
|
||||
self.assertIs(pixels, render_out)
|
||||
self.assertFalse(np.all(render_out == 0))
|
||||
|
||||
failing_render_size = (10, 10)
|
||||
self.assertNotEqual(failing_render_size, render_size)
|
||||
with self.assertRaises(ValueError):
|
||||
pixels = renderer.render(
|
||||
out=np.zeros((*failing_render_size, 3), np.uint8)
|
||||
)
|
||||
failing_render_size = (10, 10)
|
||||
self.assertNotEqual(failing_render_size, render_size)
|
||||
with self.assertRaises(ValueError):
|
||||
renderer.render(out=np.zeros((*failing_render_size, 3), np.uint8))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "mujoco"
|
||||
version = "3.0.2"
|
||||
version = "3.1.2"
|
||||
authors = [
|
||||
{name = "Google DeepMind", email = "mujoco@deepmind.com"},
|
||||
]
|
||||
@@ -36,9 +36,9 @@ dynamic = ["readme", "scripts"]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://github.com/google-deepmind/mujoco"
|
||||
Documentation = "https://mujoco.readthedocs.io/en/3.0.2"
|
||||
Documentation = "https://mujoco.readthedocs.io/en/3.1.2"
|
||||
Repository = "https://github.com/google-deepmind/mujoco"
|
||||
Changelog = "https://mujoco.readthedocs.io/en/3.0.2/changelog.html"
|
||||
Changelog = "https://mujoco.readthedocs.io/en/3.1.2/changelog.html"
|
||||
|
||||
[tool.setuptools]
|
||||
include-package-data = false
|
||||
|
||||
Reference in New Issue
Block a user