MCPcopy Create free account
hub / github.com/URLab-Sim/UnrealRoboticsLab / materialize_inline_meshes

Function materialize_inline_meshes

Scripts/clean_meshes.py:220–290  ·  view source on GitHub ↗

Rewrite `` `` (MuJoCo's inline-data form) to `` `` by emitting an OBJ next to the rest of the mesh assets. Returns the number of inline meshes materialized. MuJoCo accepts meshes declared inline via ``vertex`` (flat list of 3-tuple floats

(root, mesh_base: Path)

Source from the content-addressed store, hash-verified

218
219
220def materialize_inline_meshes(root, mesh_base: Path) -> int:
221 """Rewrite ``<mesh vertex="..." face="...">`` (MuJoCo&#x27;s inline-data form)
222 to ``<mesh file="...">`` by emitting an OBJ next to the rest of the
223 mesh assets. Returns the number of inline meshes materialized.
224
225 MuJoCo accepts meshes declared inline via ``vertex`` (flat list of
226 3-tuple floats) and ``face`` (flat list of 3-tuple ints) attributes
227 instead of an external ``file``. Unreal's importer can't follow the
228 inline form, so we synthesize a real OBJ on disk and rewrite the
229 ``<mesh>`` element to point at it. The downstream conversion phase
230 then produces a GLB companion just like any other ``file=`` mesh.
231 """
232 materialized = 0
233 mesh_base.mkdir(parents=True, exist_ok=True)
234 for mesh_el in list(root.iter("mesh")):
235 if mesh_el.get("file"):
236 continue
237 vertex_attr = mesh_el.get("vertex")
238 face_attr = mesh_el.get("face")
239 if not vertex_attr or not face_attr:
240 continue
241
242 name = mesh_el.get("name")
243 if not name:
244 print(f" x Inline mesh has no name; skipping (cannot synthesize filename)")
245 continue
246
247 try:
248 verts = _parse_floats(vertex_attr)
249 faces = [int(t) for t in face_attr.replace(",", " ").split() if t]
250 except ValueError as e:
251 print(f" x Failed to parse vertex/face for '{name}': {e}")
252 continue
253
254 if len(verts) % 3 != 0 or len(faces) % 3 != 0:
255 print(f" x Inline mesh '{name}' has non-triangular layout "
256 f"(verts={len(verts)}, faces={len(faces)}) — skipping")
257 continue
258
259 n_verts = len(verts) // 3
260 n_faces = len(faces) // 3
261
262 # Filename-safe stem. MuJoCo names can contain '/' (the scene XML
263 # uses slash-paths heavily) which would create stray directories.
264 safe_stem = name.replace("/", "_").replace("\\", "_").replace(":", "_")
265 obj_path = mesh_base / f"{safe_stem}.obj"
266
267 print(f"\n[inline] Materializing '{name}' -> {obj_path.name} "
268 f"({n_verts} verts, {n_faces} faces)")
269
270 # Standard OBJ — vertices are 1-indexed in OBJ but 0-indexed in MuJoCo.
271 with open(obj_path, "w", encoding="utf-8") as f:
272 f.write(f"# Generated by clean_meshes.py from inline MJCF mesh '{name}'\n")
273 for i in range(n_verts):
274 f.write(f"v {verts[i*3]:.6f} {verts[i*3+1]:.6f} {verts[i*3+2]:.6f}\n")
275 for i in range(n_faces):
276 a, b, c = faces[i*3] + 1, faces[i*3+1] + 1, faces[i*3+2] + 1
277 f.write(f"f {a} {b} {c}\n")

Callers 1

process_xmlFunction · 0.85

Calls 1

_parse_floatsFunction · 0.85

Tested by

no test coverage detected