Source code for isaaclab.sim.spawners.meshes.meshes
# Copyright (c) 2022-2025, The Isaac Lab Project Developers.# All rights reserved.## SPDX-License-Identifier: BSD-3-Clausefrom__future__importannotationsimportnumpyasnpimporttrimeshimporttrimesh.transformationsfromtypingimportTYPE_CHECKINGimportisaacsim.core.utils.primsasprim_utilsfrompxrimportUsd,UsdPhysicsfromisaaclab.simimportschemasfromisaaclab.sim.utilsimportbind_physics_material,bind_visual_material,clonefrom..materialsimportDeformableBodyMaterialCfg,RigidBodyMaterialCfgifTYPE_CHECKING:from.importmeshes_cfg
[docs]@clonedefspawn_mesh_sphere(prim_path:str,cfg:meshes_cfg.MeshSphereCfg,translation:tuple[float,float,float]|None=None,orientation:tuple[float,float,float,float]|None=None,)->Usd.Prim:"""Create a USD-Mesh sphere prim with the given attributes. .. note:: This function is decorated with :func:`clone` that resolves prim path into list of paths if the input prim path is a regex pattern. This is done to support spawning multiple assets from a single and cloning the USD prim at the given path expression. Args: prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, then the asset is spawned at all the matching prim paths. cfg: The configuration instance. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to the origin. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to identity. Returns: The created prim. Raises: ValueError: If a prim already exists at the given path. """# create a trimesh spheresphere=trimesh.creation.uv_sphere(radius=cfg.radius)# spawn the sphere as a mesh_spawn_mesh_geom_from_mesh(prim_path,cfg,sphere,translation,orientation)# return the primreturnprim_utils.get_prim_at_path(prim_path)
[docs]@clonedefspawn_mesh_cuboid(prim_path:str,cfg:meshes_cfg.MeshCuboidCfg,translation:tuple[float,float,float]|None=None,orientation:tuple[float,float,float,float]|None=None,)->Usd.Prim:"""Create a USD-Mesh cuboid prim with the given attributes. .. note:: This function is decorated with :func:`clone` that resolves prim path into list of paths if the input prim path is a regex pattern. This is done to support spawning multiple assets from a single and cloning the USD prim at the given path expression. Args: prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, then the asset is spawned at all the matching prim paths. cfg: The configuration instance. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to the origin. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to identity. Returns: The created prim. Raises: ValueError: If a prim already exists at the given path. """# create a trimesh boxbox=trimesh.creation.box(cfg.size)# spawn the cuboid as a mesh_spawn_mesh_geom_from_mesh(prim_path,cfg,box,translation,orientation,None)# return the primreturnprim_utils.get_prim_at_path(prim_path)
[docs]@clonedefspawn_mesh_cylinder(prim_path:str,cfg:meshes_cfg.MeshCylinderCfg,translation:tuple[float,float,float]|None=None,orientation:tuple[float,float,float,float]|None=None,)->Usd.Prim:"""Create a USD-Mesh cylinder prim with the given attributes. .. note:: This function is decorated with :func:`clone` that resolves prim path into list of paths if the input prim path is a regex pattern. This is done to support spawning multiple assets from a single and cloning the USD prim at the given path expression. Args: prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, then the asset is spawned at all the matching prim paths. cfg: The configuration instance. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to the origin. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to identity. Returns: The created prim. Raises: ValueError: If a prim already exists at the given path. """# align axis from "Z" to input by rotating the cylinderaxis=cfg.axis.upper()ifaxis=="X":transform=trimesh.transformations.rotation_matrix(np.pi/2,[0,1,0])elifaxis=="Y":transform=trimesh.transformations.rotation_matrix(-np.pi/2,[1,0,0])else:transform=None# create a trimesh cylindercylinder=trimesh.creation.cylinder(radius=cfg.radius,height=cfg.height,transform=transform)# spawn the cylinder as a mesh_spawn_mesh_geom_from_mesh(prim_path,cfg,cylinder,translation,orientation)# return the primreturnprim_utils.get_prim_at_path(prim_path)
[docs]@clonedefspawn_mesh_capsule(prim_path:str,cfg:meshes_cfg.MeshCapsuleCfg,translation:tuple[float,float,float]|None=None,orientation:tuple[float,float,float,float]|None=None,)->Usd.Prim:"""Create a USD-Mesh capsule prim with the given attributes. .. note:: This function is decorated with :func:`clone` that resolves prim path into list of paths if the input prim path is a regex pattern. This is done to support spawning multiple assets from a single and cloning the USD prim at the given path expression. Args: prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, then the asset is spawned at all the matching prim paths. cfg: The configuration instance. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to the origin. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to identity. Returns: The created prim. Raises: ValueError: If a prim already exists at the given path. """# align axis from "Z" to input by rotating the cylinderaxis=cfg.axis.upper()ifaxis=="X":transform=trimesh.transformations.rotation_matrix(np.pi/2,[0,1,0])elifaxis=="Y":transform=trimesh.transformations.rotation_matrix(-np.pi/2,[1,0,0])else:transform=None# create a trimesh capsulecapsule=trimesh.creation.capsule(radius=cfg.radius,height=cfg.height,transform=transform)# spawn capsule if it doesn't exist._spawn_mesh_geom_from_mesh(prim_path,cfg,capsule,translation,orientation)# return the primreturnprim_utils.get_prim_at_path(prim_path)
[docs]@clonedefspawn_mesh_cone(prim_path:str,cfg:meshes_cfg.MeshConeCfg,translation:tuple[float,float,float]|None=None,orientation:tuple[float,float,float,float]|None=None,)->Usd.Prim:"""Create a USD-Mesh cone prim with the given attributes. .. note:: This function is decorated with :func:`clone` that resolves prim path into list of paths if the input prim path is a regex pattern. This is done to support spawning multiple assets from a single and cloning the USD prim at the given path expression. Args: prim_path: The prim path or pattern to spawn the asset at. If the prim path is a regex pattern, then the asset is spawned at all the matching prim paths. cfg: The configuration instance. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to the origin. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to identity. Returns: The created prim. Raises: ValueError: If a prim already exists at the given path. """# align axis from "Z" to input by rotating the cylinderaxis=cfg.axis.upper()ifaxis=="X":transform=trimesh.transformations.rotation_matrix(np.pi/2,[0,1,0])elifaxis=="Y":transform=trimesh.transformations.rotation_matrix(-np.pi/2,[1,0,0])else:transform=None# create a trimesh conecone=trimesh.creation.cone(radius=cfg.radius,height=cfg.height,transform=transform)# spawn cone if it doesn't exist._spawn_mesh_geom_from_mesh(prim_path,cfg,cone,translation,orientation)# return the primreturnprim_utils.get_prim_at_path(prim_path)
"""Helper functions."""def_spawn_mesh_geom_from_mesh(prim_path:str,cfg:meshes_cfg.MeshCfg,mesh:trimesh.Trimesh,translation:tuple[float,float,float]|None=None,orientation:tuple[float,float,float,float]|None=None,scale:tuple[float,float,float]|None=None,):"""Create a `USDGeomMesh`_ prim from the given mesh. This function is similar to :func:`shapes._spawn_geom_from_prim_type` but spawns the prim from a given mesh. In case of the mesh, it is spawned as a USDGeomMesh prim with the given vertices and faces. There is a difference in how the properties are applied to the prim based on the type of object: - Deformable body properties: The properties are applied to the mesh prim: ``{prim_path}/geometry/mesh``. - Collision properties: The properties are applied to the mesh prim: ``{prim_path}/geometry/mesh``. - Rigid body properties: The properties are applied to the parent prim: ``{prim_path}``. Args: prim_path: The prim path to spawn the asset at. cfg: The config containing the properties to apply. mesh: The mesh to spawn the prim from. translation: The translation to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to the origin. orientation: The orientation in (w, x, y, z) to apply to the prim w.r.t. its parent prim. Defaults to None, in which case this is set to identity. scale: The scale to apply to the prim. Defaults to None, in which case this is set to identity. Raises: ValueError: If a prim already exists at the given path. ValueError: If both deformable and rigid properties are used. ValueError: If both deformable and collision properties are used. ValueError: If the physics material is not of the correct type. Deformable properties require a deformable physics material, and rigid properties require a rigid physics material. .. _USDGeomMesh: https://openusd.org/dev/api/class_usd_geom_mesh.html """# spawn geometry if it doesn't exist.ifnotprim_utils.is_prim_path_valid(prim_path):prim_utils.create_prim(prim_path,prim_type="Xform",translation=translation,orientation=orientation)else:raiseValueError(f"A prim already exists at path: '{prim_path}'.")# check that invalid schema types are not usedifcfg.deformable_propsisnotNoneandcfg.rigid_propsisnotNone:raiseValueError("Cannot use both deformable and rigid properties at the same time.")ifcfg.deformable_propsisnotNoneandcfg.collision_propsisnotNone:raiseValueError("Cannot use both deformable and collision properties at the same time.")# check material types are correctifcfg.deformable_propsisnotNoneandcfg.physics_materialisnotNone:ifnotisinstance(cfg.physics_material,DeformableBodyMaterialCfg):raiseValueError("Deformable properties require a deformable physics material.")ifcfg.rigid_propsisnotNoneandcfg.physics_materialisnotNone:ifnotisinstance(cfg.physics_material,RigidBodyMaterialCfg):raiseValueError("Rigid properties require a rigid physics material.")# create all the paths we need for claritygeom_prim_path=prim_path+"/geometry"mesh_prim_path=geom_prim_path+"/mesh"# create the mesh primmesh_prim=prim_utils.create_prim(mesh_prim_path,prim_type="Mesh",scale=scale,attributes={"points":mesh.vertices,"faceVertexIndices":mesh.faces.flatten(),"faceVertexCounts":np.asarray([3]*len(mesh.faces)),"subdivisionScheme":"bilinear",},)# note: in case of deformable objects, we need to apply the deformable properties to the mesh prim.# this is different from rigid objects where we apply the properties to the parent prim.ifcfg.deformable_propsisnotNone:# apply mass propertiesifcfg.mass_propsisnotNone:schemas.define_mass_properties(mesh_prim_path,cfg.mass_props)# apply deformable body propertiesschemas.define_deformable_body_properties(mesh_prim_path,cfg.deformable_props)elifcfg.collision_propsisnotNone:# decide on type of collision approximation based on the meshifcfg.__class__.__name__=="MeshSphereCfg":collision_approximation="boundingSphere"elifcfg.__class__.__name__=="MeshCuboidCfg":collision_approximation="boundingCube"else:# for: MeshCylinderCfg, MeshCapsuleCfg, MeshConeCfgcollision_approximation="convexHull"# apply collision approximation to mesh# note: for primitives, we use the convex hull approximation -- this should be sufficient for most cases.mesh_collision_api=UsdPhysics.MeshCollisionAPI.Apply(mesh_prim)mesh_collision_api.GetApproximationAttr().Set(collision_approximation)# apply collision propertiesschemas.define_collision_properties(mesh_prim_path,cfg.collision_props)# apply visual materialifcfg.visual_materialisnotNone:ifnotcfg.visual_material_path.startswith("/"):material_path=f"{geom_prim_path}/{cfg.visual_material_path}"else:material_path=cfg.visual_material_path# create materialcfg.visual_material.func(material_path,cfg.visual_material)# apply materialbind_visual_material(mesh_prim_path,material_path)# apply physics materialifcfg.physics_materialisnotNone:ifnotcfg.physics_material_path.startswith("/"):material_path=f"{geom_prim_path}/{cfg.physics_material_path}"else:material_path=cfg.physics_material_path# create materialcfg.physics_material.func(material_path,cfg.physics_material)# apply materialbind_physics_material(mesh_prim_path,material_path)# note: we apply the rigid properties to the parent prim in case of rigid objects.ifcfg.rigid_propsisnotNone:# apply mass propertiesifcfg.mass_propsisnotNone:schemas.define_mass_properties(prim_path,cfg.mass_props)# apply rigid propertiesschemas.define_rigid_body_properties(prim_path,cfg.rigid_props)