2323COLLISION_GEOM_GROUP = 3
2424VISUAL_GEOM_GROUP = 0
2525
26- def _add_geoms_after_conversion (body , name , pose , conversion_output ,
27- is_visual ):
28- geom_list = []
29- for path , info in conversion_output .obj_files .items ():
30- geom = body .add (
31- "geom" ,
32- name = su .find_unique_name (body , "geom" , name ),
33- pos = su .vec3d_to_list (pose .pos ()),
34- euler = su .quat_to_euler_list (pose .rot ()),
35- )
36- geom .type = "mesh"
37- file_without_extension = os .path .splitext (
38- os .path .basename (path ))[0 ]
39- asset_loaded = geom .root .asset .find ('mesh' , file_without_extension )
40- if asset_loaded is None :
41- geom .mesh = geom .root .asset .add ('mesh' , file = path )
42- if is_visual :
43- # Some visual meshes are too thin and can cause inertia
44- # calculation in Mujoco to fail. Mark them as "shell" to prevent
45- # Mujoco from failing in compilation. The visual geometry will
46- # not be used for inertia computation anyway since we explicitly
47- # convert inertia from sdformat to mjcf.
48- # https://github.com/google-deepmind/mujoco/issues/2455
49- geom .mesh .inertia = "shell"
50- mat_asset = geom .root .asset .add ("material" ,
51- name = "material_" + file_without_extension ,
52- specular = info .mat .specular ,
53- shininess = info .mat .shininess ,
54- rgba = info .mat .rgba )
55- geom .material = mat_asset
56- else :
57- geom .mesh = asset_loaded
58- return geom_list
26+ def _set_mesh_inertia (mjcf_mesh , is_visual ):
27+ if is_visual :
28+ # Some visual meshes are too thin and can cause inertia
29+ # calculation in Mujoco to fail. Mark them as "shell" to prevent
30+ # Mujoco from failing in compilation. The visual geometry will
31+ # not be used for inertia computation anyway since we explicitly
32+ # convert inertia from sdformat to mjcf.
33+ # https://github.com/google-deepmind/mujoco/issues/2455
34+ mjcf_mesh .inertia = "shell"
35+
5936
6037def _validate_uri (uri ):
6138 if 'http://' in uri or 'https://' in uri :
@@ -66,7 +43,8 @@ def _validate_uri(uri):
6643 return uri .replace (prefix , '' , 1 )
6744 return uri
6845
69- def _is_unsupported_mesh_geo (root , sdf_geom ):
46+
47+ def _is_unsupported_mesh_geo (sdf_geom ):
7048 if not sdf_geom .mesh_shape ():
7149 return False
7250 mesh_shape = sdf_geom .mesh_shape ()
@@ -79,6 +57,7 @@ def _is_unsupported_mesh_geo(root, sdf_geom):
7957 # for Mujoco.
8058 return extension not in [".stl" , ".msh" ]
8159
60+
8261def add_geometry (body , name , pose , sdf_geom , is_visual = False ):
8362 """
8463 Converts an SDFormat geometry to an MJCF geom and add it to the given body.
@@ -135,7 +114,7 @@ def add_geometry(body, name, pose, sdf_geom, is_visual=False):
135114 if _is_unsupported_mesh_geo (sdf_geom ):
136115 raise RuntimeError (
137116 f"Call `convert_and_add_mesh` for unsupported mesh geo { uri } " )
138- extension_tokens = os .path .basename (mesh_shape . uri () ).split ("." )
117+ extension_tokens = os .path .basename (uri ).split ("." )
139118 if (len (extension_tokens ) == 1 ):
140119 raise RuntimeError ("Unable to find the mesh extension {}"
141120 .format (uri ))
@@ -145,26 +124,9 @@ def add_geometry(body, name, pose, sdf_geom, is_visual=False):
145124 if asset_loaded is None :
146125 dirname = os .path .dirname (mesh_shape .file_path ())
147126 mesh_file_path = os .path .join (dirname , uri )
148- try :
149- geom .mesh = geom .root .asset .add ('mesh' ,
150- file = mesh_file_path )
151- except ValueError as e :
152- # Try converting mesh to .obj. This could result in multiple
153- # geos, one per mesh in the input file.
154- converted_path = os .path .join (dirname ,
155- file_without_extension + ".obj" )
156- print (f"Converting { mesh_file_path } to { converted_path } " )
157- result = convert_mesh_to_obj_multimesh (mesh_file_path ,
158- converted_path )
159- if isinstance (result , str ):
160- geom .mesh = geom .root .asset .add ('mesh' ,
161- file = result )
162- else :
163- geom .remove ()
164- return _add_geoms_after_conversion (body , name , pose ,
165- result , is_visual )
166- if is_visual :
167- geom .mesh .inertia = "shell"
127+ geom .mesh = geom .root .asset .add ('mesh' ,
128+ file = mesh_file_path )
129+ _set_mesh_inertia (geom .mesh , is_visual )
168130 else :
169131 geom .mesh = asset_loaded
170132 geom .mesh .scale = su .vec3d_to_list (mesh_shape .scale ())
@@ -174,41 +136,36 @@ def add_geometry(body, name, pose, sdf_geom, is_visual=False):
174136
175137 return geom
176138
139+
177140def _add_mesh_geom_with_assets (body , name , pose , mjcf_mesh_asset ,
178- is_visual = False , mjcf_material_asset = None ):
141+ mjcf_material_asset = None ):
179142 geom = body .add (
180143 "geom" ,
181144 name = su .find_unique_name (body , "geom" , name ),
182145 pos = su .vec3d_to_list (pose .pos ()),
183146 euler = su .quat_to_euler_list (pose .rot ()),
184147 )
185148 geom .type = "mesh"
186- if is_visual :
187- # Some visual meshes are too thin and can cause inertia
188- # calculation in Mujoco to fail. Mark them as "shell" to prevent
189- # Mujoco from failing in compilation. The visual geometry will
190- # not be used for inertia computation anyway since we explicitly
191- # convert inertia from sdformat to mjcf.
192- # https://github.com/google-deepmind/mujoco/issues/2455
193- geom .mesh .inertia = "shell"
194149 geom .mesh = mjcf_mesh_asset
195150 if mjcf_material_asset :
196151 geom .material = mjcf_material_asset
197152 return geom
198153
154+
199155def convert_and_add_mesh (body , name , pose , sdf_mesh , is_visual = False ):
200156 # Check if asset was loaded already with the uri key. If so, just add the
201- # asset:
157+ # asset. This can happen if the converted mesh has a single sub-mesh,
158+ # which was loaded already.
202159 uri = _validate_uri (sdf_mesh .uri ())
203160 file_without_extension = os .path .splitext (os .path .basename (uri ))[0 ]
204161 mesh_asset_name = file_without_extension
205162 mesh_loaded = body .root .asset .find ('mesh' , mesh_asset_name )
206163 material_asset_name = "material_" + file_without_extension
207- material_loaded = body .root .asset .find ('mesh ' , material_asset_name )
164+ material_loaded = body .root .asset .find ('material ' , material_asset_name )
208165 if mesh_loaded :
209- return _add_mesh_geom_with_assets (body , name , pose , mesh_loaded ,
210- is_visual ,
166+ geom = _add_mesh_geom_with_assets (body , name , pose , mesh_loaded ,
211167 mjcf_material_asset = material_loaded )
168+ return [geom ]
212169
213170 # Try converting mesh to sanitized .obj. This could result in multiple
214171 # geos, one per mesh in the input file.
@@ -224,50 +181,34 @@ def convert_and_add_mesh(body, name, pose, sdf_mesh, is_visual=False):
224181 for path , info in result .obj_files .items ():
225182 output_file_without_extension = os .path .splitext (
226183 os .path .basename (path ))[0 ]
227- sub_mesh_loaded = geom .root .asset .find ('mesh' ,
184+ sub_mesh_loaded = body .root .asset .find ('mesh' ,
228185 output_file_without_extension )
229186 sub_mesh_material_asset_name = (
230187 "material_" + output_file_without_extension )
231- material_loaded = body .root .asset .find ('mesh ' ,
188+ material_loaded = body .root .asset .find ('material ' ,
232189 sub_mesh_material_asset_name )
233190 if sub_mesh_loaded :
234191 geom_list .append (
235192 _add_mesh_geom_with_assets (body , name , pose , mesh_loaded ,
236- is_visual ,
237193 mjcf_material_asset = material_loaded ))
194+ continue
238195
196+ # Add mesh and material assets
197+ mesh = body .root .asset .add ('mesh' , file = path )
198+ _set_mesh_inertia (mesh , is_visual )
199+ mesh .scale = su .vec3d_to_list (sdf_mesh .scale ())
200+ material_asset = None
201+ if is_visual :
202+ material_asset = body .root .asset .add ("material" ,
203+ name = sub_mesh_material_asset_name ,
204+ specular = info .mat .specular ,
205+ shininess = info .mat .shininess ,
206+ rgba = info .mat .rgba )
207+ geom_list .append (
208+ _add_mesh_geom_with_assets (body , name , pose , mesh_loaded ,
209+ mjcf_material_asset = material_asset ))
239210
240- geom = body .add (
241- "geom" ,
242- name = su .find_unique_name (body , "geom" , name ),
243- pos = su .vec3d_to_list (pose .pos ()),
244- euler = su .quat_to_euler_list (pose .rot ()),
245- )
246- geom .type = "mesh"
247- file_without_extension = os .path .splitext (
248- os .path .basename (path ))[0 ]
249- asset_loaded = geom .root .asset .find ('mesh' , file_without_extension )
250- if asset_loaded is None :
251- geom .mesh = geom .root .asset .add ('mesh' , file = path )
252- if is_visual :
253- # Some visual meshes are too thin and can cause inertia
254- # calculation in Mujoco to fail. Mark them as "shell" to prevent
255- # Mujoco from failing in compilation. The visual geometry will
256- # not be used for inertia computation anyway since we explicitly
257- # convert inertia from sdformat to mjcf.
258- # https://github.com/google-deepmind/mujoco/issues/2455
259- geom .mesh .inertia = "shell"
260- mat_asset = geom .root .asset .add ("material" ,
261- name = "material_" + file_without_extension ,
262- specular = info .mat .specular ,
263- shininess = info .mat .shininess ,
264- rgba = info .mat .rgba )
265- geom .material = mat_asset
266- else :
267- geom .mesh = asset_loaded
268211 return geom_list
269- return _add_geoms_after_conversion (body , name , pose ,
270- result , is_visual )
271212
272213
273214def apply_surface_to_geometry (geom , sdf_surface ):
@@ -297,10 +238,20 @@ def add_collision(body, col):
297238 """
298239 sem_pose = col .semantic_pose ()
299240 pose = su .graph_resolver .resolve_pose (sem_pose )
300- geom = add_geometry (body , col .name (), pose , col .geometry ())
301- geom .group = COLLISION_GEOM_GROUP
302- apply_surface_to_geometry (geom , col .surface ())
303- return geom
241+ if _is_unsupported_mesh_geo (col .geometry ()):
242+ sdf_mesh = col .geometry ().mesh_shape ()
243+ geoms = convert_and_add_mesh (body , col .name (), pose , sdf_mesh ,
244+ is_visual = False )
245+ else :
246+ geom = add_geometry (body , col .name (), pose , col .geometry (),
247+ is_visual = False )
248+ geoms = [geom ]
249+ for geom in geoms :
250+ geom .group = COLLISION_GEOM_GROUP
251+ apply_surface_to_geometry (geom , col .surface ())
252+ if len (geoms ) == 1 :
253+ return geoms [0 ]
254+ return geoms
304255
305256
306257def add_visual (body , vis ):
@@ -316,17 +267,25 @@ def add_visual(body, vis):
316267 """
317268 sem_pose = vis .semantic_pose ()
318269 pose = su .graph_resolver .resolve_pose (sem_pose )
319- geoms = add_geometry (body , vis .name (), pose , vis .geometry (), is_visual = True )
320- if not isinstance (geoms , list ):
321- geoms = [geoms ]
270+ if _is_unsupported_mesh_geo (vis .geometry ()):
271+ sdf_mesh = vis .geometry ().mesh_shape ()
272+ geoms = convert_and_add_mesh (body , vis .name (), pose , sdf_mesh ,
273+ is_visual = True )
274+ else :
275+ geom = add_geometry (body , vis .name (), pose , vis .geometry (),
276+ is_visual = True )
277+ geoms = [geom ]
278+
322279 for geom in geoms :
323280 geom .group = VISUAL_GEOM_GROUP
324281 # Visual geoms do not collide with any other geom, so we set their
325282 # contype and conaffinity to 0.
326283 geom .contype = 0
327284 geom .conaffinity = 0
328285 if vis .material () is not None :
329- mjcf_mat = add_material (geom , vis .material ())
286+ mjcf_mat = add_material (geom . root , vis .material ())
330287 for geom in geoms :
331288 geom .material = mjcf_mat
289+ if len (geoms ) == 1 :
290+ return geoms [0 ]
332291 return geoms
0 commit comments