@@ -11,12 +11,16 @@ def __init__(
1111 self ,
1212 model ,
1313 data ,
14+ mode = 'window' ,
1415 title = "mujoco-python-viewer" ,
1516 width = None ,
1617 height = None ,
1718 hide_menus = True ):
1819 self .model = model
1920 self .data = data
21+ self .render_mode = mode
22+ if self .render_mode not in ['offscreen' , 'window' ]:
23+ raise NotImplementedError ("Invalid mode. Only 'offscreen' and 'window' are supported." )
2024
2125 self .is_alive = True
2226
@@ -25,6 +29,8 @@ def __init__(
2529 self ._button_right_pressed = False
2630 self ._left_double_click_pressed = False
2731 self ._right_double_click_pressed = False
32+ self ._last_left_click_time = None
33+ self ._last_right_click_time = None
2834 self ._last_mouse_x = 0
2935 self ._last_mouse_y = 0
3036 self ._paused = False
@@ -54,24 +60,28 @@ def __init__(
5460 if not height :
5561 _ , height = glfw .get_video_mode (glfw .get_primary_monitor ()).size
5662
63+ if self .render_mode == 'offscreen' :
64+ glfw .window_hint (glfw .VISIBLE , 0 )
65+
5766 self .window = glfw .create_window (
5867 width , height , title , None , None )
5968 glfw .make_context_current (self .window )
6069 glfw .swap_interval (1 )
6170
6271 framebuffer_width , framebuffer_height = glfw .get_framebuffer_size (
6372 self .window )
64- window_width , _ = glfw .get_window_size (self .window )
65- self ._scale = framebuffer_width * 1.0 / window_width
66-
67- # set callbacks
68- glfw .set_cursor_pos_callback (self .window , self ._cursor_pos_callback )
69- glfw .set_mouse_button_callback (
70- self .window , self ._mouse_button_callback )
71- glfw .set_scroll_callback (self .window , self ._scroll_callback )
72- glfw .set_key_callback (self .window , self ._key_callback )
73- self ._last_left_click_time = None
74- self ._last_right_click_time = None
73+
74+ # install callbacks only for 'window' mode
75+ if self .render_mode == 'window' :
76+ window_width , _ = glfw .get_window_size (self .window )
77+ self ._scale = framebuffer_width * 1.0 / window_width
78+
79+ # set callbacks
80+ glfw .set_cursor_pos_callback (self .window , self ._cursor_pos_callback )
81+ glfw .set_mouse_button_callback (
82+ self .window , self ._mouse_button_callback )
83+ glfw .set_scroll_callback (self .window , self ._scroll_callback )
84+ glfw .set_key_callback (self .window , self ._key_callback )
7585
7686 # create options, camera, scene, context
7787 self .vopt = mujoco .MjvOption ()
@@ -484,7 +494,43 @@ def apply_perturbations(self):
484494 mujoco .mjv_applyPerturbPose (self .model , self .data , self .pert , 0 )
485495 mujoco .mjv_applyPerturbForce (self .model , self .data , self .pert )
486496
497+ def read_pixels (self , camid = None ):
498+ if self .render_mode is 'window' :
499+ raise NotImplementedError (
500+ "Use 'render()' in 'window' mode." )
501+
502+ if camid is not None :
503+ if camid == - 1 :
504+ self .cam .type = mujoco .mjtCamera .mjCAMERA_FREE
505+ else :
506+ self .cam .type = mujoco .mjtCamera .mjCAMERA_FIXED
507+ self .cam .fixedcamid = camid
508+
509+ self .viewport .width , self .viewport .height = glfw .get_framebuffer_size (
510+ self .window )
511+ # update scene
512+ mujoco .mjv_updateScene (
513+ self .model ,
514+ self .data ,
515+ self .vopt ,
516+ self .pert ,
517+ self .cam ,
518+ mujoco .mjtCatBit .mjCAT_ALL .value ,
519+ self .scn )
520+ # render
521+ mujoco .mjr_render (self .viewport , self .scn , self .ctx )
522+
523+ img = np .zeros (
524+ (glfw .get_framebuffer_size (
525+ self .window )[1 ], glfw .get_framebuffer_size (
526+ self .window )[0 ], 3 ), dtype = np .uint8 )
527+ mujoco .mjr_readPixels (img , None , self .viewport , self .ctx )
528+ return np .flipud (img )
529+
487530 def render (self ):
531+ if self .render_mode is 'offscreen' :
532+ raise NotImplementedError (
533+ "Use 'read_pixels()' for 'offscreen' mode." )
488534 if not self .is_alive :
489535 raise Exception (
490536 "GLFW window does not exist but you tried to render." )
0 commit comments