Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions python/mujoco/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,66 @@ def update_scene(
self._scene,
)

def update_hfield(self, hfieldid: int) -> None:
"""Uploads modified height field data to the GPU.

Args:
hfieldid: The ID of the height field to update.

Raises:
RuntimeError: If called after the renderer is closed.
ValueError: If hfieldid is out of range.
"""
if self._mjr_context is None:
raise RuntimeError('update_hfield cannot be called after close.')
if hfieldid < 0 or hfieldid >= self._model.nhfield:
raise ValueError(
f'hfieldid {hfieldid} is out of range [0, {self._model.nhfield}).'
)
if self._gl_context:
self._gl_context.make_current()
_render.mjr_uploadHField(self._model, self._mjr_context, hfieldid)

def update_mesh(self, meshid: int) -> None:
"""Uploads modified mesh data to the GPU.

Args:
meshid: The ID of the mesh to update.

Raises:
RuntimeError: If called after the renderer is closed.
ValueError: If meshid is out of range.
"""
if self._mjr_context is None:
raise RuntimeError('update_mesh cannot be called after close.')
if meshid < 0 or meshid >= self._model.nmesh:
raise ValueError(
f'meshid {meshid} is out of range [0, {self._model.nmesh}).'
)
if self._gl_context:
self._gl_context.make_current()
_render.mjr_uploadMesh(self._model, self._mjr_context, meshid)

def update_texture(self, texid: int) -> None:
"""Uploads modified texture data to the GPU.

Args:
texid: The ID of the texture to update.

Raises:
RuntimeError: If called after the renderer is closed.
ValueError: If texid is out of range.
"""
if self._mjr_context is None:
raise RuntimeError('update_texture cannot be called after close.')
if texid < 0 or texid >= self._model.ntex:
raise ValueError(
f'texid {texid} is out of range [0, {self._model.ntex}).'
)
if self._gl_context:
self._gl_context.make_current()
_render.mjr_uploadTexture(self._model, self._mjr_context, texid)

def close(self) -> None:
"""Frees the resources used by the renderer.

Expand Down
157 changes: 157 additions & 0 deletions python/mujoco/renderer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,5 +158,162 @@ def test_renderer_output_with_out(self):
renderer.render(out=np.zeros((*failing_render_size, 3), np.uint8))


def test_update_hfield(self):
xml = """
<mujoco>
<asset>
<hfield name="terrain" nrow="10" ncol="10" size="1 1 0.1 0.1"/>
</asset>
<worldbody>
<geom type="hfield" hfield="terrain"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
with mujoco.Renderer(model, 50, 50) as renderer:
# Should succeed for valid hfield id
renderer.update_hfield(0)

def test_update_hfield_out_of_range(self):
xml = """
<mujoco>
<asset>
<hfield name="terrain" nrow="10" ncol="10" size="1 1 0.1 0.1"/>
</asset>
<worldbody>
<geom type="hfield" hfield="terrain"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
with mujoco.Renderer(model, 50, 50) as renderer:
with self.assertRaisesRegex(ValueError, 'out of range'):
renderer.update_hfield(-1)
with self.assertRaisesRegex(ValueError, 'out of range'):
renderer.update_hfield(1)

def test_update_hfield_after_close(self):
xml = """
<mujoco>
<asset>
<hfield name="terrain" nrow="10" ncol="10" size="1 1 0.1 0.1"/>
</asset>
<worldbody>
<geom type="hfield" hfield="terrain"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
renderer = mujoco.Renderer(model, 50, 50)
renderer.close()
with self.assertRaisesRegex(RuntimeError, 'after close'):
renderer.update_hfield(0)

def test_update_mesh(self):
xml = """
<mujoco>
<asset>
<mesh name="box" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
</asset>
<worldbody>
<geom type="mesh" mesh="box"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
with mujoco.Renderer(model, 50, 50) as renderer:
# Should succeed for valid mesh id
renderer.update_mesh(0)

def test_update_mesh_out_of_range(self):
xml = """
<mujoco>
<asset>
<mesh name="box" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
</asset>
<worldbody>
<geom type="mesh" mesh="box"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
with mujoco.Renderer(model, 50, 50) as renderer:
with self.assertRaisesRegex(ValueError, 'out of range'):
renderer.update_mesh(-1)
with self.assertRaisesRegex(ValueError, 'out of range'):
renderer.update_mesh(1)

def test_update_mesh_after_close(self):
xml = """
<mujoco>
<asset>
<mesh name="box" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
</asset>
<worldbody>
<geom type="mesh" mesh="box"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
renderer = mujoco.Renderer(model, 50, 50)
renderer.close()
with self.assertRaisesRegex(RuntimeError, 'after close'):
renderer.update_mesh(0)

def test_update_texture(self):
xml = """
<mujoco>
<asset>
<texture name="grid" type="2d" builtin="checker" width="32" height="32"/>
<material name="grid" texture="grid"/>
</asset>
<worldbody>
<geom type="plane" size="1 1 0.1" material="grid"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
with mujoco.Renderer(model, 50, 50) as renderer:
# Should succeed for valid texture id
renderer.update_texture(0)

def test_update_texture_out_of_range(self):
xml = """
<mujoco>
<asset>
<texture name="grid" type="2d" builtin="checker" width="32" height="32"/>
<material name="grid" texture="grid"/>
</asset>
<worldbody>
<geom type="plane" size="1 1 0.1" material="grid"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
with mujoco.Renderer(model, 50, 50) as renderer:
with self.assertRaisesRegex(ValueError, 'out of range'):
renderer.update_texture(-1)
with self.assertRaisesRegex(ValueError, 'out of range'):
renderer.update_texture(1)

def test_update_texture_after_close(self):
xml = """
<mujoco>
<asset>
<texture name="grid" type="2d" builtin="checker" width="32" height="32"/>
<material name="grid" texture="grid"/>
</asset>
<worldbody>
<geom type="plane" size="1 1 0.1" material="grid"/>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
renderer = mujoco.Renderer(model, 50, 50)
renderer.close()
with self.assertRaisesRegex(RuntimeError, 'after close'):
renderer.update_texture(0)


if __name__ == '__main__':
absltest.main()