Skip to content

Commit 0b8e805

Browse files
Wheels WuWheels Wu
authored andcommitted
Add update_hfield, update_mesh, update_texture methods to Renderer class.
1 parent de47cf0 commit 0b8e805

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

python/mujoco/renderer.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,66 @@ def update_scene(
304304
self._scene,
305305
)
306306

307+
def update_hfield(self, hfieldid: int) -> None:
308+
"""Uploads modified height field data to the GPU.
309+
310+
Args:
311+
hfieldid: The ID of the height field to update.
312+
313+
Raises:
314+
RuntimeError: If called after the renderer is closed.
315+
ValueError: If hfieldid is out of range.
316+
"""
317+
if self._mjr_context is None:
318+
raise RuntimeError('update_hfield cannot be called after close.')
319+
if hfieldid < 0 or hfieldid >= self._model.nhfield:
320+
raise ValueError(
321+
f'hfieldid {hfieldid} is out of range [0, {self._model.nhfield}).'
322+
)
323+
if self._gl_context:
324+
self._gl_context.make_current()
325+
_render.mjr_uploadHField(self._model, self._mjr_context, hfieldid)
326+
327+
def update_mesh(self, meshid: int) -> None:
328+
"""Uploads modified mesh data to the GPU.
329+
330+
Args:
331+
meshid: The ID of the mesh to update.
332+
333+
Raises:
334+
RuntimeError: If called after the renderer is closed.
335+
ValueError: If meshid is out of range.
336+
"""
337+
if self._mjr_context is None:
338+
raise RuntimeError('update_mesh cannot be called after close.')
339+
if meshid < 0 or meshid >= self._model.nmesh:
340+
raise ValueError(
341+
f'meshid {meshid} is out of range [0, {self._model.nmesh}).'
342+
)
343+
if self._gl_context:
344+
self._gl_context.make_current()
345+
_render.mjr_uploadMesh(self._model, self._mjr_context, meshid)
346+
347+
def update_texture(self, texid: int) -> None:
348+
"""Uploads modified texture data to the GPU.
349+
350+
Args:
351+
texid: The ID of the texture to update.
352+
353+
Raises:
354+
RuntimeError: If called after the renderer is closed.
355+
ValueError: If texid is out of range.
356+
"""
357+
if self._mjr_context is None:
358+
raise RuntimeError('update_texture cannot be called after close.')
359+
if texid < 0 or texid >= self._model.ntex:
360+
raise ValueError(
361+
f'texid {texid} is out of range [0, {self._model.ntex}).'
362+
)
363+
if self._gl_context:
364+
self._gl_context.make_current()
365+
_render.mjr_uploadTexture(self._model, self._mjr_context, texid)
366+
307367
def close(self) -> None:
308368
"""Frees the resources used by the renderer.
309369

python/mujoco/renderer_test.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,5 +158,162 @@ def test_renderer_output_with_out(self):
158158
renderer.render(out=np.zeros((*failing_render_size, 3), np.uint8))
159159

160160

161+
def test_update_hfield(self):
162+
xml = """
163+
<mujoco>
164+
<asset>
165+
<hfield name="terrain" nrow="10" ncol="10" size="1 1 0.1 0.1"/>
166+
</asset>
167+
<worldbody>
168+
<geom type="hfield" hfield="terrain"/>
169+
</worldbody>
170+
</mujoco>
171+
"""
172+
model = mujoco.MjModel.from_xml_string(xml)
173+
with mujoco.Renderer(model, 50, 50) as renderer:
174+
# Should succeed for valid hfield id
175+
renderer.update_hfield(0)
176+
177+
def test_update_hfield_out_of_range(self):
178+
xml = """
179+
<mujoco>
180+
<asset>
181+
<hfield name="terrain" nrow="10" ncol="10" size="1 1 0.1 0.1"/>
182+
</asset>
183+
<worldbody>
184+
<geom type="hfield" hfield="terrain"/>
185+
</worldbody>
186+
</mujoco>
187+
"""
188+
model = mujoco.MjModel.from_xml_string(xml)
189+
with mujoco.Renderer(model, 50, 50) as renderer:
190+
with self.assertRaisesRegex(ValueError, 'out of range'):
191+
renderer.update_hfield(-1)
192+
with self.assertRaisesRegex(ValueError, 'out of range'):
193+
renderer.update_hfield(1)
194+
195+
def test_update_hfield_after_close(self):
196+
xml = """
197+
<mujoco>
198+
<asset>
199+
<hfield name="terrain" nrow="10" ncol="10" size="1 1 0.1 0.1"/>
200+
</asset>
201+
<worldbody>
202+
<geom type="hfield" hfield="terrain"/>
203+
</worldbody>
204+
</mujoco>
205+
"""
206+
model = mujoco.MjModel.from_xml_string(xml)
207+
renderer = mujoco.Renderer(model, 50, 50)
208+
renderer.close()
209+
with self.assertRaisesRegex(RuntimeError, 'after close'):
210+
renderer.update_hfield(0)
211+
212+
def test_update_mesh(self):
213+
xml = """
214+
<mujoco>
215+
<asset>
216+
<mesh name="box" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
217+
</asset>
218+
<worldbody>
219+
<geom type="mesh" mesh="box"/>
220+
</worldbody>
221+
</mujoco>
222+
"""
223+
model = mujoco.MjModel.from_xml_string(xml)
224+
with mujoco.Renderer(model, 50, 50) as renderer:
225+
# Should succeed for valid mesh id
226+
renderer.update_mesh(0)
227+
228+
def test_update_mesh_out_of_range(self):
229+
xml = """
230+
<mujoco>
231+
<asset>
232+
<mesh name="box" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
233+
</asset>
234+
<worldbody>
235+
<geom type="mesh" mesh="box"/>
236+
</worldbody>
237+
</mujoco>
238+
"""
239+
model = mujoco.MjModel.from_xml_string(xml)
240+
with mujoco.Renderer(model, 50, 50) as renderer:
241+
with self.assertRaisesRegex(ValueError, 'out of range'):
242+
renderer.update_mesh(-1)
243+
with self.assertRaisesRegex(ValueError, 'out of range'):
244+
renderer.update_mesh(1)
245+
246+
def test_update_mesh_after_close(self):
247+
xml = """
248+
<mujoco>
249+
<asset>
250+
<mesh name="box" vertex="0 0 0 1 0 0 0 1 0 0 0 1"/>
251+
</asset>
252+
<worldbody>
253+
<geom type="mesh" mesh="box"/>
254+
</worldbody>
255+
</mujoco>
256+
"""
257+
model = mujoco.MjModel.from_xml_string(xml)
258+
renderer = mujoco.Renderer(model, 50, 50)
259+
renderer.close()
260+
with self.assertRaisesRegex(RuntimeError, 'after close'):
261+
renderer.update_mesh(0)
262+
263+
def test_update_texture(self):
264+
xml = """
265+
<mujoco>
266+
<asset>
267+
<texture name="grid" type="2d" builtin="checker" width="32" height="32"/>
268+
<material name="grid" texture="grid"/>
269+
</asset>
270+
<worldbody>
271+
<geom type="plane" size="1 1 0.1" material="grid"/>
272+
</worldbody>
273+
</mujoco>
274+
"""
275+
model = mujoco.MjModel.from_xml_string(xml)
276+
with mujoco.Renderer(model, 50, 50) as renderer:
277+
# Should succeed for valid texture id
278+
renderer.update_texture(0)
279+
280+
def test_update_texture_out_of_range(self):
281+
xml = """
282+
<mujoco>
283+
<asset>
284+
<texture name="grid" type="2d" builtin="checker" width="32" height="32"/>
285+
<material name="grid" texture="grid"/>
286+
</asset>
287+
<worldbody>
288+
<geom type="plane" size="1 1 0.1" material="grid"/>
289+
</worldbody>
290+
</mujoco>
291+
"""
292+
model = mujoco.MjModel.from_xml_string(xml)
293+
with mujoco.Renderer(model, 50, 50) as renderer:
294+
with self.assertRaisesRegex(ValueError, 'out of range'):
295+
renderer.update_texture(-1)
296+
with self.assertRaisesRegex(ValueError, 'out of range'):
297+
renderer.update_texture(1)
298+
299+
def test_update_texture_after_close(self):
300+
xml = """
301+
<mujoco>
302+
<asset>
303+
<texture name="grid" type="2d" builtin="checker" width="32" height="32"/>
304+
<material name="grid" texture="grid"/>
305+
</asset>
306+
<worldbody>
307+
<geom type="plane" size="1 1 0.1" material="grid"/>
308+
</worldbody>
309+
</mujoco>
310+
"""
311+
model = mujoco.MjModel.from_xml_string(xml)
312+
renderer = mujoco.Renderer(model, 50, 50)
313+
renderer.close()
314+
with self.assertRaisesRegex(RuntimeError, 'after close'):
315+
renderer.update_texture(0)
316+
317+
161318
if __name__ == '__main__':
162319
absltest.main()

0 commit comments

Comments
 (0)