Deformable Bodies (Beta)#

This documentation introduces the new deformable body beta feature based on two new USD schemas: the Omni Physics Deformable Schema, which is staged for becoming part of the standard USD Physics Schema, and PhysX specific schema extensions in PhysX Schema Addition. The new schemas and implementations replace the deprecated deformable body schema and implementation as well as the particle cloth feature described here.

To enable the deformable beta feature in the Omniverse Kit UI, see Enable Deformable Schema Beta.

For information on how to migrate deprecated schema code, functions in omni.physx.scripts.deformableUtils or USD assets, see Deformable Migration Guide.

Single Mesh Surface Deformable#

To create a single mesh surface deformable, a UsdGeom.Mesh, limited to triangular faces, is required that can serve as a simulation mesh:

from pxr import Usd, UsdGeom, Vt, Gf

def create_trimesh(stage, path, res):
    triMesh = UsdGeom.Mesh.Define(stage, path)
    step = 1.0 / res
    verts = [(i * step, j * step, 0.0) for j in range(res + 1) for i in range(res + 1)]
    idx = lambda i, j: j * (res + 1) + i
    tris = [(idx(i,j), idx(i+1,j), idx(i+1,j+1)) + (idx(i,j), idx(i+1,j+1), idx(i,j+1))
            for j in range(res) for i in range(res)]
    triMesh.GetPointsAttr().Set(Vt.Vec3fArray(verts))
    triMesh.GetFaceVertexCountsAttr().Set([3] * (2 * res**2))
    triMesh.GetFaceVertexIndicesAttr().Set([i for t in tris for i in t])
    return triMesh

Once a suitable triangle mesh is available a single mesh surface deformable can be defined by applying OmniPhysicsDeformableBodyAPI, OmniPhysicsSurfaceDeformableSimAPI and UsdPhysics.CollisionAPI. PhysX specific attributes can be set by applying PhysxSurfaceDeformableBodyAPI and PhysxSchema.PhysxCollisionAPI. The following code snippet additionally shows how to apply APIs and set attributes based on codeless schemas:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd

stage = omni.usd.get_context().get_stage()
triMesh = create_trimesh(stage, "/World/TriMesh", 6)
prim = triMesh.GetPrim()

# Apply deformable body API and set mass
prim.ApplyAPI("OmniPhysicsDeformableBodyAPI")
if prim.HasAPI("OmniPhysicsDeformableBodyAPI"):
    prim.GetAttribute("omniphysics:mass").Set(0.5)

# Apply volume simulation API and set rest shape properties
prim.ApplyAPI("OmniPhysicsSurfaceDeformableSimAPI")
if prim.HasAPI("OmniPhysicsSurfaceDeformableSimAPI"):
    restShapePointsAttr = prim.GetAttribute("omniphysics:restShapePoints")
    restShapePointsAttr.Set(triMesh.GetPointsAttr().Get())
    restTriVtxIndicesAttr = prim.GetAttribute("omniphysics:restTriVtxIndices")
    # Convert face vertex indices to Gf.Vec3i indices
    triVtxIndices = list(zip(*[iter(triMesh.GetFaceVertexIndicesAttr().Get())]*3))
    restTriVtxIndicesAttr.Set(triVtxIndices)

# Apply collision API
collisionAPI = UsdPhysics.CollisionAPI.Apply(prim)

# Optionally set physx specific APIs and properties

prim.ApplyAPI("PhysxSurfaceDeformableBodyAPI")
if prim.HasAPI("PhysxSurfaceDeformableBodyAPI"):
    prim.GetAttribute("physxDeformableBody:disableGravity").Set(True)
    prim.GetAttribute("physxDeformableBody:selfCollision").Set(True)

physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(prim)
if physxCollisionAPI:
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)
Snippet dependencies: create_trimesh.

While the Omni Physics Deformable Schema allows the specification of a deformable body with zero, one or multiple colliders, the Omni PhysX implementation is limited to deformable bodies with exactly one collider. Therefore, in the case of a single mesh deformable body, adding a UsdPhysics.CollisionAPI to the mesh is mandatory.

Omni Physics Deformable Schema allows for a rest shape topology (restTriVtxIndices) that diverges from the simulation mesh topology (faceVertexIndices), however, Omni PhysX is currently limited to accepting only identical topologies.

The basic setup can be simplified by using omni.physx.scripts.deformableUtils.set_physics_surface_deformable_body:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils

stage = omni.usd.get_context().get_stage()
triMesh = create_trimesh(stage, "/World/TriMesh", 6)
prim = triMesh.GetPrim()

# Call deformableUtils helper
success = deformableUtils.set_physics_surface_deformable_body(stage, prim.GetPath())

# Apply customizations
if prim.HasAPI("OmniPhysicsDeformableBodyAPI"):
    prim.GetAttribute("omniphysics:mass").Set(0.5)

prim.ApplyAPI("PhysxSurfaceDeformableBodyAPI")
if prim.HasAPI("PhysxSurfaceDeformableBodyAPI"):
    prim.GetAttribute("physxDeformableBody:disableGravity").Set(True)
    prim.GetAttribute("physxDeformableBody:selfCollision").Set(True)

physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(prim)
if physxCollisionAPI:
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)

Surface Deformable Hierarchy#

This section provides detailed steps for creating a surface deformable hierarchy in Omni PhysX. The following components need to be specified:

  • Root: Using a UsdGeom.Xform prim enables the specification of a coordinate space within which the deformable will be simulated.

  • Simulation mesh: A UsdGeom.Mesh prim, limited to triangular faces, is used to define the simulation state and the rest shape of the deformable body.

  • Collision mesh: For surface deformable bodies, using a separate collision mesh is not supported. Instead the simulation mesh needs to be marked for collision detection.

Optionally one can specify:

  • Graphics geometry: Any number of UsdGeom.PointBased prims can be used to visualize the deformable.

If a suitable simulation mesh is not available, it can be helpful to have the simulation mesh automatically generated from a given UsdGeom.Mesh prim, which is covered in Auto Surface Deformable Hierarchy.

In order to demonstrate the flexibility around render meshes a helper is introduced to create a quad mesh:

def create_quadmesh(stage, path, res):
    quadMesh  = UsdGeom.Mesh.Define(stage, path)
    step  = 1.0 / res
    v  = [(i*step, j*step, 0.0) for j in range(res+1) for i in range(res+1)]
    idx = lambda i, j: j*(res+1)+i
    quads  = [(idx(i,j), idx(i+1,j), idx(i+1,j+1), idx(i,j+1))
        for j in range(res) for i in range(res)]
    quadMesh.GetPointsAttr().Set(Vt.Vec3fArray(v))
    quadMesh.GetFaceVertexCountsAttr().Set([4]*(res*res))
    quadMesh.GetFaceVertexIndicesAttr().Set([p for quad in quads for p in quad])
    return quadMesh

The following example shows how a hierarchy can be set up:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd

stage = omni.usd.get_context().get_stage()

# Create a root prim
rootXform = UsdGeom.Xform.Define(stage, "/World/DeformableBody")
rootPrim = rootXform.GetPrim()
rootPrim.ApplyAPI("OmniPhysicsDeformableBodyAPI")
if rootPrim.HasAPI("OmniPhysicsDeformableBodyAPI"):
    rootPrim.GetAttribute("omniphysics:mass").Set(0.5)

# Apply customizations
rootPrim.ApplyAPI("PhysxSurfaceDeformableBodyAPI")
if rootPrim.HasAPI("PhysxSurfaceDeformableBodyAPI"):
    rootPrim.GetAttribute("physxDeformableBody:disableGravity").Set(True)
    rootPrim.GetAttribute("physxDeformableBody:selfCollision").Set(True)

# Define a simulation mesh
simPath = "/World/DeformableBody/SimulationMesh"
simMesh = create_trimesh(stage, simPath, 6)
simPrim = simMesh.GetPrim()
simPrim.ApplyAPI("OmniPhysicsSurfaceDeformableSimAPI")

if simPrim.HasAPI("OmniPhysicsSurfaceDeformableSimAPI"):
    restShapePointsAttr = simPrim.GetAttribute("omniphysics:restShapePoints")
    restShapePointsAttr.Set(simMesh.GetPointsAttr().Get())
    triVtxIndices = list(zip(*[iter(simMesh.GetFaceVertexIndicesAttr().Get())]*3))
    restTriVtxIndicesAttr = simPrim.GetAttribute("omniphysics:restTriVtxIndices")
    restTriVtxIndicesAttr.Set(triVtxIndices)

# Add bind pose for registering other meshes with respect to sim mesh
purposesAttrName = "deformablePose:custom:omniphysics:purposes"
pointsAttrName = "deformablePose:custom:omniphysics:points"
simPrim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "custom")
if simPrim.HasAPI("OmniPhysicsDeformablePoseAPI", "custom"):
    simPrim.GetAttribute(purposesAttrName).Set(["bindPose"])
    simPrim.GetAttribute(pointsAttrName).Set(simMesh.GetPointsAttr().Get())

# Mark the simulation mesh for collision detection
collisionAPI = UsdPhysics.CollisionAPI.Apply(simPrim)

# Apply customizations
physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(simPrim)
if (physxCollisionAPI):
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)

# Don't render simulation mesh
simMesh.GetPurposeAttr().Set("guide")

# Optionally add a render mesh
renderPath = "/World/DeformableBody/RenderMesh"
renderMesh = create_quadmesh(stage, renderPath, 12)
renderPrim = renderMesh.GetPrim()

# Add bind pose for registering configuration with respect to sim mesh
renderPrim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "custom")
if renderPrim.HasAPI("OmniPhysicsDeformablePoseAPI", "custom"):
    renderPrim.GetAttribute(purposesAttrName).Set(["bindPose"])
    renderPrim.GetAttribute(pointsAttrName).Set(renderMesh.GetPointsAttr().Get())
Snippet dependencies: create_trimesh, create_quadmesh.

Auto Surface Deformable Hierarchy#

Instead of the manual setup shown above, omni.physx.scripts.deformableUtils.create_auto_surface_deformable_hierarchy can be used to automatically generate a suitable simulation mesh from a given UsdGeom.Mesh source mesh. For instance, it is helpful when the provided mesh consists of quads or is excessively high-resolution. The function sets up the hierarchy using a PhysxAutoDeformableBodyAPI:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils
from omni.physx import get_physx_cooking_interface

stage = omni.usd.get_context().get_stage()

# Setup source mesh for generation of simulation mesh
cookingSrcPath = "/World/CookingSrcMesh"
cookingSrcMesh = create_quadmesh(stage, cookingSrcPath, 6)
cookingSrcMesh.MakeInvisible()

# Create a root prim
rootXform = UsdGeom.Xform.Define(stage, "/World/DeformableBody")
rootPrim = rootXform.GetPrim()

# Create render mesh, could be multiple
renderPath = "/World/DeformableBody/RenderMesh"
renderMesh = create_quadmesh(stage, renderPath, 12)

success = deformableUtils.create_auto_surface_deformable_hierarchy(
    stage,
    rootPrim.GetPath(),
    "/World/DeformableBody/SimulationMesh",
    cookingSrcPath,
    cooking_src_simplification_enabled=False,
    set_visibility_with_guide_purpose=True
)

# Apply customizations to root
rootPrim.ApplyAPI("PhysxSurfaceDeformableBodyAPI")
if rootPrim.HasAPI("PhysxSurfaceDeformableBodyAPI"):
    rootPrim.GetAttribute("physxDeformableBody:disableGravity").Set(True)
    rootPrim.GetAttribute("physxDeformableBody:selfCollision").Set(True)

# Apply customizations to sim mesh
simPrim = stage.GetPrimAtPath("/World/DeformableBody/SimulationMesh")
physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(simPrim)
if physxCollisionAPI:
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)

# Trigger synchronous generation of simulation mesh data
get_physx_cooking_interface().cook_auto_deformable_body(str(rootPrim.GetPath()))
Snippet dependencies: create_quadmesh.

See implementation of create_auto_surface_deformable_hierarchy for details on how to set up a PhysxAutoDeformableBodyAPI manually.

Note that the cooking process is triggered synchronously in the example. This is not required if no immediate access to generated mesh data is required. The mesh data is always guaranteed to be available before the simulation starts.

Single Mesh Volume Deformable#

In order to create a single mesh volume deformable, a UsdGeom.TetMesh prim is required that can serve as a simulation mesh. Here is an example that shows how to set up a simple mesh forming a cube:

from pxr import Usd, UsdGeom, Vt, Gf

def create_simulation_tetmesh(stage, path, add_surface=False):

    # Create a tet mesh prim
    tetMesh = UsdGeom.TetMesh.Define(stage, path)

    # Set the points
    points = Vt.Vec3fArray([
        (0, 0, 0), (1, 0, 0), (1, 1, 0), (0, 1, 0),
        (0, 0, 1), (1, 0, 1), (1, 1, 1), (0, 1, 1),
    ])

    # Define three tetrahedra
    tetVtxIndices = Vt.Vec4iArray([
        (0, 1, 3, 4), (1, 2, 3, 6), (1, 4, 5, 6), (3, 4, 6, 7), (1, 3, 4, 6),
    ])

    tetMesh.GetPointsAttr().Set(points)
    tetMesh.GetTetVertexIndicesAttr().Set(tetVtxIndices)

    # Adding surface information for collision detection
    if add_surface:
        surfaceFaceVertexIndices = UsdGeom.TetMesh.ComputeSurfaceFaces(tetMesh)
        tetMesh.GetSurfaceFaceVertexIndicesAttr().Set(surfaceFaceVertexIndices)

    return tetMesh

Once a tetrahedron mesh is available a single mesh volume deformable can be defined by applying OmniPhysicsDeformableBodyAPI, OmniPhysicsVolumeDeformableSimAPI and UsdPhysics.CollisionAPI. PhysX specific attributes can be set by applying PhysxBaseDeformableBodyAPI and PhysxSchema.PhysxCollisionAPI. The following code snippet additionally shows how to apply APIs and set attributes based on codeless schemas:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd

stage = omni.usd.get_context().get_stage()

tetMesh = create_simulation_tetmesh(stage, "/World/TetMesh", add_surface=True)
prim = tetMesh.GetPrim()

# Apply deformable body API and set mass
prim.ApplyAPI("OmniPhysicsDeformableBodyAPI")
if prim.HasAPI("OmniPhysicsDeformableBodyAPI"):
    prim.GetAttribute("omniphysics:mass").Set(30.0)

# Apply volume simulation API and set rest shape properties
prim.ApplyAPI("OmniPhysicsVolumeDeformableSimAPI")
if prim.HasAPI("OmniPhysicsVolumeDeformableSimAPI"):
    restShapePointsAttr = prim.GetAttribute("omniphysics:restShapePoints")
    restShapePointsAttr.Set(tetMesh.GetPointsAttr().Get())
    restTetVtxIndicesAttr = prim.GetAttribute("omniphysics:restTetVtxIndices")
    restTetVtxIndicesAttr.Set(tetMesh.GetTetVertexIndicesAttr().Get())

# Apply collision API
collisionAPI = UsdPhysics.CollisionAPI.Apply(prim)

# Optionally set physx specific APIs and properties

prim.ApplyAPI("PhysxBaseDeformableBodyAPI")
if prim.HasAPI("PhysxBaseDeformableBodyAPI"):
    prim.GetAttribute("physxDeformableBody:disableGravity").Set(True)

physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(prim)
if physxCollisionAPI:
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)
Snippet dependencies: create_simulation_tetmesh.

While the Omni Physics Deformable Schema allows the specification of a deformable body with zero, one or multiple colliders, the Omni PhysX implementation is limited to deformable bodies with exactly one collider. Therefore, in the case of a single mesh deformable body, adding a UsdPhysics.CollisionAPI to the mesh is mandatory.

Omni Physics Deformable Schema allows for a rest shape topology (restTetVtxIndices) that diverges from the simulation mesh topology (tetVertexIndices), however, Omni PhysX is currently limited to accepting only identical topologies.

The basic setup can be simplified by using omni.physx.scripts.deformableUtils.set_physics_volume_deformable_body:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils

stage = omni.usd.get_context().get_stage()
tetMesh = create_simulation_tetmesh(stage, "/World/TetMesh", add_surface=True)
prim = tetMesh.GetPrim()

# Call deformableUtils helper
success = deformableUtils.set_physics_volume_deformable_body(stage, prim.GetPath())

# Apply customizations
if (prim.HasAPI("OmniPhysicsDeformableBodyAPI")):
    prim.GetAttribute("omniphysics:mass").Set(30.0)

prim.ApplyAPI("PhysxBaseDeformableBodyAPI")
if (prim.HasAPI("PhysxBaseDeformableBodyAPI")):
    prim.GetAttribute("physxDeformableBody:disableGravity").Set(True)

physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(prim)
if (physxCollisionAPI):
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)
Snippet dependencies: create_simulation_tetmesh.

Volume Deformable Hierarchy#

This section provides detailed steps for creating a volume deformable hierarchy in Omni PhysX. The following components need to be specified:

  • Root: Using a UsdGeom.Xform prim enables the specification of a coordinate space within which the deformable will be simulated.

  • Simulation mesh: A UsdGeom.TetMesh prim is used to define the simulation state and the rest shape of the deformable body.

Optionally one can also specify:

  • Collision mesh: Optionally a separate UsdGeom.TetMesh prim is used to define geometry used for collision detection. If no separate collision mesh is specified, it’s mandatory to mark the simulation mesh for collision detection.

  • Graphics geometry: Any number of UsdGeom.PointBased prims can be used to visualize the deformable.

If suitable simulation and collision meshes are not available, it can be helpful to have the meshes automatically generated from a given UsdGeom.Mesh prim, which is covered in Auto Volume Deformable Hierarchy.

First a helper function to create a collision tetmesh is defined:

from pxr import Usd, UsdGeom, Vt, Gf

def create_collision_tetmesh(stage, path):

    # Create a tet mesh prim
    tetMesh = UsdGeom.TetMesh.Define(stage, path)

    points = Vt.Vec3fArray([
        (0.134, 0.134, 0.134),  #  0: bottom-front-left
        (0.866, 0.134, 0.134),  #  1: bottom-front-right
        (0.866, 0.866, 0.134),  #  2: bottom-back-right
        (0.134, 0.866, 0.134),  #  3: bottom-back-left
        (0.134, 0.134, 0.866),  #  4: top-front-left
        (0.866, 0.134, 0.866),  #  5: top-front-right
        (0.866, 0.866, 0.866),  #  6: top-back-right
        (0.134, 0.866, 0.866),  #  7: top-back-left
        (0.5, 0.5, 0.0),  #  8: bottom face center
        (0.5, 0.5, 1.0),  #  9: top face center
        (0.0, 0.5, 0.5),  # 10: left face center
        (1.0, 0.5, 0.5),  # 11: right face center
        (0.5, 0.0, 0.5),  # 12: front face center
        (0.5, 1.0, 0.5),  # 13: back face center
        (0.5, 0.5, 0.5),  # 14 center
    ])

    tetVtxIndices = Vt.Vec4iArray([
        (0, 1, 8, 14), (1, 2, 8, 14), (2, 3, 8, 14), (3, 0, 8, 14), # bottom tets
        (4, 5, 9, 14), (5, 6, 9, 14), (6, 7, 9, 14), (7, 4, 9, 14), # top tets
        (0, 1, 12, 14), (1, 5, 12, 14), (5, 4, 12, 14), (4, 0, 12, 14), # front tets
        (3, 2, 13, 14), (2, 6, 13, 14), (6, 7, 13, 14), (7, 3, 13, 14), # back tets
        (0, 3, 10, 14), (3, 10, 7, 14), (7, 10, 4, 14), (4, 10, 0, 14), # left tets
        (1, 2, 11, 14), (2, 11, 6, 14), (6, 11, 5, 14), (5, 11, 1, 14), # right tets
    ])

    tetMesh.GetPointsAttr().Set(points)
    tetMesh.GetTetVertexIndicesAttr().Set(tetVtxIndices)

    # Adding surface information for collision detection
    surfaceFaceVertexIndices = UsdGeom.TetMesh.ComputeSurfaceFaces(tetMesh)
    tetMesh.GetSurfaceFaceVertexIndicesAttr().Set(surfaceFaceVertexIndices)

    return tetMesh

Then a helper function to create a render mesh with details is defined:

from pxr import Usd, UsdGeom, Vt, Gf
import numpy as np

def create_sphere_mesh(stage, path):
    subdivs, amplitude, freq, slope = 6, 0.01, 30.0, 0.5
    mesh = UsdGeom.Mesh.Define(stage, path)

    # Initial Octahedron
    pts = [(1.0,0.5,0.5), (0.0,0.5,0.5), (0.5,1.0,0.5),
           (0.5,0.0,0.5), (0.5,0.5,1.0), (0.5,0.5,0.0)]
    tris = [(0,2,4), (2,1,4), (1,3,4), (3,0,4), (2,0,5), (1,2,5), (3,1,5), (0,3,5)]
    mid = {}
    for _ in range(subdivs):
        new = []
        for i,j,k in tris:
            def m(a,b):
                key = tuple(sorted((a,b)))
                if key not in mid:
                    p = np.add(pts[a], pts[b]) / 2
                    mid[key] = len(pts)
                    pts.append(tuple(p))
                return mid[key]
            a,b,c = m(i,j), m(j,k), m(k,i)
            new += [(i,a,c), (j,b,a), (k,c,b), (a,b,c)]
        tris = new

    for i, p in enumerate(pts):
        v = np.array(p) - 0.5
        n = v / np.linalg.norm(v)
        theta = np.arccos(n[2])                            # [0, pi]
        phi = np.mod(np.arctan2(n[1], n[0]), 2 * np.pi)    # [0, 2π]
        fade = np.sin(theta) ** 3
        wave = amplitude * fade * np.sin(freq * (theta + slope * phi))
        pts[i] = tuple(0.5 + (0.5 + wave) * n)

    mesh.GetPointsAttr().Set(Vt.Vec3fArray(pts))
    mesh.GetFaceVertexCountsAttr().Set([3]*len(tris))
    mesh.GetFaceVertexIndicesAttr().Set([i for tri in tris for i in tri])
    return mesh

Finally the deformable body hierarchy can be created:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd

stage = omni.usd.get_context().get_stage()

# Create a root prim
rootXform = UsdGeom.Xform.Define(stage, "/World/DeformableBody")
rootPrim = rootXform.GetPrim()
rootPrim.ApplyAPI("OmniPhysicsDeformableBodyAPI")
if (rootPrim.HasAPI("OmniPhysicsDeformableBodyAPI")):
    rootPrim.GetAttribute("omniphysics:mass").Set(30.0)

# Apply customizations to root
rootPrim.ApplyAPI("PhysxBaseDeformableBodyAPI")
if (rootPrim.HasAPI("PhysxBaseDeformableBodyAPI")):
    rootPrim.GetAttribute("physxDeformableBody:disableGravity").Set(True)

# Define a simulation mesh
simPath = "/World/DeformableBody/SimulationMesh"
simMesh = create_simulation_tetmesh(stage, simPath, add_surface=False)
simPrim = simMesh.GetPrim()
simPrim.ApplyAPI("OmniPhysicsVolumeDeformableSimAPI")

if (simPrim.HasAPI("OmniPhysicsVolumeDeformableSimAPI")):
    restShapePointsAttr = simPrim.GetAttribute("omniphysics:restShapePoints")
    restShapePointsAttr.Set(simMesh.GetPointsAttr().Get())
    restTetVtxIndicesAttr = simPrim.GetAttribute("omniphysics:restTetVtxIndices")
    restTetVtxIndicesAttr.Set(simMesh.GetTetVertexIndicesAttr().Get())

# Add bind pose for registering other meshes with respect to sim mesh
purposesAttrName = "deformablePose:custom:omniphysics:purposes"
pointsAttrName = "deformablePose:custom:omniphysics:points"
simPrim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "custom")
if simPrim.HasAPI("OmniPhysicsDeformablePoseAPI", "custom"):
    simPrim.GetAttribute(purposesAttrName).Set(["bindPose"])
    simPrim.GetAttribute(pointsAttrName).Set(simMesh.GetPointsAttr().Get())

# Define a separate collision mesh (instead we could also apply the
# CollisionAPI to the simPrim)
collisionPath = "/World/DeformableBody/CollisionMesh"
collisionMesh = create_collision_tetmesh(stage, collisionPath)
collisionPrim = collisionMesh.GetPrim()
collisionAPI = UsdPhysics.CollisionAPI.Apply(collisionPrim)

# Add bind pose for registering configuration with respect to sim mesh
collisionPrim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "custom")
if collisionPrim.HasAPI("OmniPhysicsDeformablePoseAPI", "custom"):
    collisionPrim.GetAttribute(purposesAttrName).Set(["bindPose"])
    collisionPrim.GetAttribute(pointsAttrName).Set(collisionMesh.GetPointsAttr().Get())

# Apply customizations to collision mesh
collisionPrim.ApplyAPI("PhysxCollisionAPI")
if (collisionPrim.HasAPI("PhysxCollisionAPI")):
    collisionPrim.GetAttribute("physxCollision:contactOffset").Set(0.02)
    collisionPrim.GetAttribute("physxCollision:restOffset").Set(0.01)

# Optionally add a render mesh
renderPath = "/World/DeformableBody/RenderMesh"
renderMesh = create_sphere_mesh(stage, renderPath)

# Add bind pose for registering configuration with respect to sim mesh
renderPrim.ApplyAPI("OmniPhysicsDeformablePoseAPI", "custom")
if renderPrim.HasAPI("OmniPhysicsDeformablePoseAPI", "custom"):
    renderPrim.GetAttribute(purposesAttrName).Set(["bindPose"])
    renderPrim.GetAttribute(pointsAttrName).Set(renderMesh.GetPointsAttr().Get())

Auto Volume Deformable Hierarchy#

Instead of the manual setup shown above, omni.physx.scripts.deformableUtils.create_auto_volume_deformable_hierarchy can be used to automatically generate suitable simulation and collision tetrahedral meshes from a given source UsdGeom.Mesh prim. This is helpful especially since tetrahedral meshes are often not available and need to be generated from graphical assets. The function sets up the hierarchy using a PhysxAutoDeformableBodyAPI:

from pxr import Usd, UsdGeom, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils
from omni.physx import get_physx_cooking_interface

stage = omni.usd.get_context().get_stage()

# Create a root prim
rootXform = UsdGeom.Xform.Define(stage, "/World/DeformableBody")
rootPrim = rootXform.GetPrim()

# Create render mesh, could be multiple
renderPath = "/World/DeformableBody/RenderMesh"
renderMesh = create_sphere_mesh(stage, renderPath)

success = deformableUtils.create_auto_volume_deformable_hierarchy(
    stage = stage,
    root_prim_path = rootPrim.GetPath(),
    simulation_tetmesh_path = "/World/DeformableBody/SimulationMesh",
    collision_tetmesh_path = "/World/DeformableBody/CollisionMesh",
    cooking_src_mesh_path = renderPath,
    simulation_hex_mesh_enabled = True,
    cooking_src_simplification_enabled = True,
    set_visibility_with_guide_purpose = True
)

if (rootPrim.HasAPI("OmniPhysicsDeformableBodyAPI")):
    rootPrim.GetAttribute("omniphysics:mass").Set(30.0)

# Apply customizations to root
rootPrim.ApplyAPI("PhysxBaseDeformableBodyAPI")
if (rootPrim.HasAPI("PhysxBaseDeformableBodyAPI")):
    rootPrim.GetAttribute("physxDeformableBody:disableGravity").Set(True)

# Apply customizations to collision mesh
collisionPrim = stage.GetPrimAtPath("/World/DeformableBody/CollisionMesh")
physxCollisionAPI = PhysxSchema.PhysxCollisionAPI.Apply(collisionPrim)
if physxCollisionAPI:
    physxCollisionAPI.GetContactOffsetAttr().Set(0.02)
    physxCollisionAPI.GetRestOffsetAttr().Set(0.01)

# Trigger synchronous generation of simulation mesh data
get_physx_cooking_interface().cook_auto_deformable_body(str(rootPrim.GetPath()))
Snippet dependencies: create_sphere_mesh.

Note how in this example the render mesh within the deformable body hierarchy is used as the source cooking mesh. This is different from the previous example for surface deformable bodies.

See implementation of create_auto_volume_deformable_hierarchy for details on how to set up a PhysxAutoDeformableBodyAPI manually. If simulation_hex_mesh_enabled parameter is set to True, a PhysxAutoDeformableHexahedralMeshAPI is applied which triggers the setup of a separate simulation hexahedrally structured tet mesh. When the deformable body is instantiated in PhysX the simulation mesh is tagged as a special hexahedral mesh enabling corresponding optimizations. This feature can currently not be enabled through manual setup without PhysxAutoDeformableHexahedralMeshAPI.

Note that the cooking process is triggered synchronously in the example. This is not required if no immediate access to generated mesh data is required. The mesh data is always guaranteed to be available before the simulation starts.

Create and Assign Materials#

Physics materials define how a deformable body’s volume reacts to external forces. The Omni Physics Deformable Schema provides three API schema layers that can be stacked on a UsdShade.Material prim (see Omni Physics Deformable Schema for more details, but also note the relevant limitations of the current implementation):

PhysX specific API schemas add non-standard parameters through PhysxDeformableMaterialAPI and PhysxSurfaceDeformableMaterialAPI (elasticity damping, bend damping).

The material binding mechanism is analogous to how rigid body materials are bound, see Configure Rigid Body’s Material Properties.

The following sections show Python snippets for volume and surface deformables that each create two materials with different stiffness parameters, and bind them to separate deformable bodies.

Volume Deformables#

The following code snippet provides a helper to create a volume deformable:

from pxr import Usd, UsdGeom, UsdShade, Sdf, Vt, Gf, UsdPhysics, PhysxSchema
from omni.physx.scripts import deformableUtils

def create_volume_deformable(stage, path, offset):
    # Create a root prim
    rootXform = UsdGeom.Xform.Define(stage, path)
    rootPrim = rootXform.GetPrim()

    # Set the offset
    rootXform.AddTranslateOp().Set(offset)

    # Create render mesh, could be multiple
    renderPath = path + "/RenderMesh"
    renderMesh = create_sphere_mesh(stage, renderPath)

    success = deformableUtils.create_auto_volume_deformable_hierarchy(
        stage = stage,
        root_prim_path = rootPrim.GetPath(),
        simulation_tetmesh_path = rootPrim.GetPath().AppendChild("SimulationMesh"),
        collision_tetmesh_path = rootPrim.GetPath().AppendChild("CollisionMesh"),
        cooking_src_mesh_path = renderPath,
        simulation_hex_mesh_enabled = True,
        cooking_src_simplification_enabled = True,
        set_visibility_with_guide_purpose = True
    )

    return rootPrim
Snippet dependencies: create_sphere_mesh.

The following code snippet shows how to create a volume deformable and a suitable deformable material:

from pxr import Usd, UsdGeom, UsdShade, Sdf, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils, physicsUtils

def create_volume_material(stage, path, youngs, poissons):
    mat = UsdShade.Material.Define(stage, path)
    prim = mat.GetPrim()

    # Common parameters
    prim.ApplyAPI("OmniPhysicsBaseMaterialAPI")
    prim.GetAttribute("omniphysics:staticFriction").Set(10.0)
    prim.GetAttribute("omniphysics:dynamicFriction").Set(10.0)
    prim.GetAttribute("omniphysics:density").Set(1000.0)

    # Common deformable parameters
    prim.ApplyAPI("OmniPhysicsDeformableMaterialAPI")
    prim.GetAttribute("omniphysics:youngsModulus").Set(youngs)
    prim.GetAttribute("omniphysics:poissonsRatio").Set(poissons)

    # PhysX extras (optional)
    prim.ApplyAPI("PhysxDeformableMaterialAPI")
    prim.GetAttribute("physxDeformableMaterial:elasticityDamping").Set(0.0)
    return mat

# Get stage and add ground plane
stage = omni.usd.get_context().get_stage()
planePath = "/World/GroundPlane"
physicsUtils.add_ground_plane(stage, planePath, "Z", 25.0, Gf.Vec3f(0.0), Gf.Vec3f(0.5))

# Create deformable bodies
softDeformablePrim = create_volume_deformable(stage,
                                              "/World/DeformableBody_soft",
                                              Gf.Vec3f(0, -1, 0))

hardDeformablePrim = create_volume_deformable(stage,
                                              "/World/DeformableBody_hard",
                                              Gf.Vec3f(0,  1, 0))

# Create materials
softMatPath = "/World/DeformableMaterial_soft"
hardMatPath = "/World/DeformableMaterial_hard"
softMat = create_volume_material(stage, softMatPath, youngs=1e5,  poissons=0.45)
hardMat = create_volume_material(stage, hardMatPath, youngs=1e8,  poissons=0.45)

# Bind the materials to the deformable bodies
bindingApi = UsdShade.MaterialBindingAPI.Apply(softDeformablePrim)
bindingApi.Bind(softMat, UsdShade.Tokens.weakerThanDescendants, "physics")
bindingApi = UsdShade.MaterialBindingAPI.Apply(hardDeformablePrim)
bindingApi.Bind(hardMat, UsdShade.Tokens.weakerThanDescendants, "physics")

Note that the material as bound to the simulation mesh defines the behavior of the deformable body. Binding to the root deformable body prim works, as the material binding recursively applies to all sub prims. Currently multi material binding via UsdGeom.Subset or binding specifically to the collision mesh for surface material properties is not supported.

If the material should be general purpose (i.e. used for physics and rendering), the materialPurpose parameter of UsdShade.MaterialBindingAPI can be omitted.

Surface Deformables#

The following code snippet provides a helper to create a surface deformable:

from pxr import Usd, UsdGeom, UsdShade, Sdf, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils, physicsUtils

def create_surface_deformable(stage, path, offset, dim):
    # Create simulation mesh
    triMesh = create_trimesh(stage, path, dim)
    prim = triMesh.GetPrim()

    # Set the offset
    triMesh.AddTranslateOp().Set(offset)

    # Call deformableUtils helper
    success = deformableUtils.set_physics_surface_deformable_body(stage, prim.GetPath())

    # Apply customizations
    prim.ApplyAPI("PhysxSurfaceDeformableBodyAPI")
    if prim.HasAPI("PhysxSurfaceDeformableBodyAPI"):
        prim.GetAttribute("physxDeformableBody:selfCollision").Set(True)

    return prim
Snippet dependencies: create_trimesh.

Having a surface deformable available, a surface material is created and bound to the deformable as follows:

def create_surface_material(stage, path, youngs, poissons, thickness):
    mat = UsdShade.Material.Define(stage, path)
    prim = mat.GetPrim()

    # base + surface specific schema APIs
    prim.ApplyAPI("OmniPhysicsBaseMaterialAPI")
    prim.GetAttribute("omniphysics:dynamicFriction").Set(0.2)
    prim.GetAttribute("omniphysics:density").Set(100.0)

    prim.ApplyAPI("OmniPhysicsDeformableMaterialAPI")
    prim.GetAttribute("omniphysics:youngsModulus").Set(youngs)
    prim.GetAttribute("omniphysics:poissonsRatio").Set(poissons)

    prim.ApplyAPI("OmniPhysicsSurfaceDeformableMaterialAPI")
    prim.GetAttribute("omniphysics:surfaceThickness").Set(thickness)

    # Estimate bend stiffness from Young's modulus and Poisson's ratio
    # surfaceThickness is incorporated in PhysX
    bendStiffness = youngs/(12.0*(1.0-poissons**2))
    prim.GetAttribute("omniphysics:surfaceBendStiffness").Set(bendStiffness)

    # PhysX extras
    prim.ApplyAPI("PhysxSurfaceDeformableMaterialAPI")
    prim.GetAttribute("physxDeformableMaterial:elasticityDamping").Set(0.0)
    prim.GetAttribute("physxDeformableMaterial:bendDamping").Set(0.0)
    return mat

# Get stage and add ground plane
stage = omni.usd.get_context().get_stage()
planePath = "/World/GroundPlane"
breamPath = "/World/Beam"
physicsUtils.add_ground_plane(stage, planePath, "Z", 25.0, Gf.Vec3f(0.0), Gf.Vec3f(0.5))
physicsUtils.add_collider_box(stage, breamPath, Gf.Vec3f(0.1, 4, 0.5))

# Create deformable bodies
softDeformablePrim = create_surface_deformable(
    stage,
    "/World/DeformableBody_soft",
    Gf.Vec3f(-0.5, -1.5, 0.5),
    20
)

hardDeformablePrim = create_surface_deformable(
    stage,
    "/World/DeformableBody_hard",
    Gf.Vec3f(-0.5,  0.5, 0.5),
    20
)

# Create and bind materials
softMatPath = "/World/DeformableMaterial_soft"
hardMatPath = "/World/DeformableMaterial_hard"

softMat = create_surface_material(stage,
    softMatPath,
    youngs=5e4,
    poissons=0.45,
    thickness=0.01
)

hardMat = create_surface_material(stage,
    hardMatPath,
    youngs=2e7,
    poissons=0.45,
    thickness=0.01
)

bindingApi = UsdShade.MaterialBindingAPI.Apply(softDeformablePrim)
bindingApi.Bind(softMat, UsdShade.Tokens.weakerThanDescendants, "physics")
bindingApi = UsdShade.MaterialBindingAPI.Apply(hardDeformablePrim)
bindingApi.Bind(hardMat, UsdShade.Tokens.weakerThanDescendants, "physics")

Note that surfaceStretchStiffness and surfaceShearStiffness are not supported, and the surface bend stiffness is not currently automatically deduced from youngsModulus, poissonsRatio and surfaceThickness by the current implementation. See Material Limitations for more details.

Currently multi material binding via UsdGeom.Subset or binding specifically to the collision mesh for surface material properties is not supported.

If the material should be general purpose (i.e. used for physics and rendering), the materialPurpose parameter of UsdShade.MaterialBindingAPI can be omitted.

Material Friction Combine Modes#

The PhysX deformable solver only supports dynamic friction, and static friction values take no effect. It is recommended to use sufficiently high dynamic friction valued to emulate static friction-like behavior.

PhysX supports combine-mode logic for rigid materials. For rigid-deformable contacts, dynamic friction combine mode is supported as well. The friction combine mode needs to be configured on the rigid material using the frictionCombineMode attribute of PhysxSchema.PhysxMaterialAPI. Combine modes for deformable-deformable contacts are not supported.

Attachments and Collision Filters#

Attaching a deformable body to another deformable body, rigid body, collider or more generally to an arbitrary UsdGeom.Xformable prim involves:

The following code snippet shows how to instantiate a low-level vertex-vertex attachment:

from pxr import Usd, UsdGeom, UsdShade, Sdf, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils, physicsUtils

stage = omni.usd.get_context().get_stage()
planePath = "/World/GroundPlane"
anchorPath = "/World/Anchor"
deformablePath0 = "/World/DeformableBody0"
deformablePath1 = "/World/DeformableBody1"
attachmentPath0 = "/World/VtxXformAttachment"
attachmentPath1 = "/World/VtxVtxAttachment"
elementFilterPath = "/World/ElementCollisionFilter"

physicsUtils.add_ground_plane(stage, planePath, "Z", 25.0, Gf.Vec3f(0.0), Gf.Vec3f(0.5))

# Create anchor Xform
anchor = UsdGeom.Xform.Define(stage, anchorPath)
anchor.AddTranslateOp().Set(Gf.Vec3f(0.0, 0.0, 2.0))

# Create two surface deformable
dim = 20 # number of vertices per side
deformableBody0 = create_surface_deformable(
    stage,
    deformablePath0,
    Gf.Vec3f(0.0,0.0,2.0),
    dim-1
)
UsdGeom.Xformable(deformableBody0).AddScaleOp().Set(Gf.Vec3f(0.5, 0.5, 0.5))
deformableBody1 = create_surface_deformable(
    stage,
    deformablePath1,
    Gf.Vec3f(0.50,0.0,2.0),
    dim-1
)
UsdGeom.Xformable(deformableBody1).AddScaleOp().Set(Gf.Vec3f(0.5, 0.5, 0.5))

# Create a VtxXform attachment
attachment0 = stage.DefinePrim(attachmentPath0, "OmniPhysicsVtxXformAttachment")
attachment0.GetRelationship("omniphysics:src0").SetTargets([deformablePath0])
attachment0.GetRelationship("omniphysics:src1").SetTargets([anchorPath])
attachment0.GetAttribute("omniphysics:vtxIndicesSrc0").Set([0, dim*dim-dim])
localPositions = [Gf.Vec3f(0.0), Gf.Vec3f(0.0, 0.5, 0.0)]
attachment0.GetAttribute("omniphysics:localPositionsSrc1").Set(localPositions)

# Create a VtxVtx attachment
attachment1 = stage.DefinePrim(attachmentPath1, "OmniPhysicsVtxVtxAttachment")
attachment1.GetRelationship("omniphysics:src0").SetTargets([deformablePath0])
attachment1.GetRelationship("omniphysics:src1").SetTargets([deformablePath1])
attachment1.GetAttribute("omniphysics:vtxIndicesSrc0").Set([dim-1, dim*dim-1])
attachment1.GetAttribute("omniphysics:vtxIndicesSrc1").Set([0,dim*dim-dim])

When the example above is simulated, collisions between the two surface deformables occur at the vertex attachment sites. These could be filtered using a OmniPhysicsElementCollisionFilter prim, however this is currently not supported for two surface deformable bodies. Instead collisions could be filtered wholesale by using standard prim level group or pair filtering.

For more information on how to instantiate low level attachment and collision filter primitives, see Attachments and Element Collision Filtering.

The following examples illustrate how omni.physx.scripts.deformableUtils.create_auto_deformable_attachment can be used to create a high-level attachment based on geometric overlaps. A UsdGeom.Scope with PhysxAutoDeformableAttachmentAPI is created, containing all the necessary low-level prims as children.

Attaching a Volume Deformable to a Collider#

The following code snippet shows how to attach a volume deformable to a collider, by creating them such that their geometries overlap in world space:

from pxr import Usd, UsdGeom, UsdShade, Sdf, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils, physicsUtils

stage = omni.usd.get_context().get_stage()
planePath = "/World/GroundPlane"
colliderPath = "/World/Box"
deformablePath = "/World/DeformableBody"
attachmentPath = "/World/Attachment"

# Add a ground plane
physicsUtils.add_ground_plane(stage, planePath, "Z", 25.0, Gf.Vec3f(0.0), Gf.Vec3f(0.5))

# Create a rigid box collider
box_prim = physicsUtils.add_rigid_box(
    stage,
    colliderPath,
    size      = Gf.Vec3f(0.3),
    position  = Gf.Vec3f(0.0, 0.0, 1.0),
    density   = 0.0,
)

# Create a volume deformable
deformableBody = create_volume_deformable(
    stage,
    deformablePath,
    Gf.Vec3f(-0.5,-0.5,0.0)
)

# Create an attachment between the deformable and the collider
deformableUtils.create_auto_deformable_attachment(
    stage,
    attachmentPath,
    deformablePath,
    colliderPath,
)

# Customize high-level attachment
attachment = stage.GetPrimAtPath(attachmentPath)
prefix = "physxAutoDeformableAttachment:"
attachment.GetAttribute(prefix+"deformableVertexOverlapOffset").Set(0.05)
attachment.GetAttribute(prefix+"collisionFilteringOffset").Set(0.1)

In an analogous manner, attachments can also be created between two volume deformables, or a volume deformable and a surface deformable. Attachments between two surface deformables are currently not supported.

Sometimes it’s necessary to increase the distance up to which collision filtering is active. This can be achieved on the high-level attachment attribute collisionFilteringOffset as shown above. By default Omni PhysX chooses a value based on heuristics. For disabling collision detection completely, it is better to disable element level collision filtering entirely by setting the enableCollisionFiltering attribute to False and instead employ standard prim level group or pair filtering.

PhysxAutoDeformableAttachmentAPI attributes related to generating attachment points along collider surfaces (poisson sampling) are currently not supported.

Attaching a Surface Deformable to an Xformable#

The following code snippet shows how to attach a surface deformable to an UsdGeom.Xformable prim. By default all of the simulation mesh vertices would be attached to the coordinate frame. In order to only attach a subset of the vertices, a mask shape is used:

from pxr import Usd, UsdGeom, UsdShade, Sdf, Vt, Gf, UsdPhysics, PhysxSchema
import omni.usd
from omni.physx.scripts import deformableUtils, physicsUtils

stage = omni.usd.get_context().get_stage()
planePath = "/World/GroundPlane"
xformPath = "/World/Anchor"
deformablePath = "/World/DeformableBody"
attachmentPath = "/World/Attachment"
maskShapePath = "/World/Attachment/MaskShape"

# Add a ground plane
physicsUtils.add_ground_plane(stage, planePath, "Z", 25.0, Gf.Vec3f(0.0), Gf.Vec3f(0.5))

# Create an Xformable
xform = UsdGeom.Xform.Define(stage, xformPath)

# Create a surface deformable
deformableBody = create_surface_deformable(
    stage,
    deformablePath,
    Gf.Vec3f(-0.5,0.0,1.0),
    20
)

# Create an attachment between the deformable and the Xformable
deformableUtils.create_auto_deformable_attachment(
    stage,
    attachmentPath,
    deformablePath,
    xformPath,
    20
)
# Limit the affected vertices to those inside of a mask UsdGeom.Cube shape
maskShape = UsdGeom.Cube.Define(stage, maskShapePath)
maskShape.AddTranslateOp().Set(Gf.Vec3f(0.0, 0.0, 1.0))
maskShape.AddScaleOp().Set(Gf.Vec3f(0.5, 0.02, 0.02))
attachment = stage.GetPrimAtPath(attachmentPath)
maskShapesRel = attachment.GetRelationship("physxAutoDeformableAttachment:maskShapes")
maskShapesRel.SetTargets([maskShapePath])

Note that only UsdGeom.Sphere, UsdGeom.Box and UsdGeom.Capsule shapes are supported as mask shapes.

Omni PhysX Deformable Limitations#

This section describes limitations of the current Omni PhysX implementation. Limitations are present on multiple levels:

Collision Mesh Limitations#

  • While the Omni Physics Deformable Schema allows the specification of a deformable body with zero or multiple colliders, the Omni PhysX implementation is limited to deformable bodies with exactly one collider.

  • For surface deformable bodies, the simulation mesh needs to be used for collision detection. As opposed to volume deformable bodies, using a separate collision mesh is not supported.

Simulation Mesh and Rest Shape Limitations#

  • Kinematic animation of the simulation mesh points is supported for volume deformables only. The kinematicEnabled attribute from OmniPhysicsDeformableBodyAPI is ignored for surface deformables.

  • The Omni Physics Deformable Schema allows for a rest shape topologies that diverges from simulation mesh topologies, as long as there is a one to one correspondence between rest shape elements and simulation mesh elements (triangles or tetrahedra). Omni PhysX is currently limited to accepting only identical topologies, i.e. matching triangle or tetrahedron vertex indices and matching point counts.

  • Manipulating the rest shape at simulation time is not supported.

Material Limitations#

  • Currently multi material binding via UsdGeom.Subset or binding specifically to the collision mesh for surface material properties (i.e. static and dynamic friction) is not supported.

  • OmniPhysicsDeformableMaterialAPI staticFriction is currently not supported for both PhysX volume and surface deformables. Sufficiently high dynamicFriction values are recommended to achieve similar behavior.

  • OmniPhysicsSurfaceDeformableMaterialAPI override parameters are currently not supported as advertised by the Omni Physics Deformable Schema documentation - youngsModulus, poissonsRatio of OmniPhysicsDeformableMaterialAPI are meant to define all surface stiffnesses applied to the volume given by the geometry and surfaceThickness by default. The surfaceStretchStiffness, surfaceShearStiffness and surfaceBendStiffness parameters are meant to be specific overrides of that default, if set to non-zero values. In the current implementation however:

    • surfaceStretchStiffness and surfaceShearStiffness are not supported at all for in plane elasticity.

    • surfaceBendStiffness is the only way to define resistance to bending. PhysX derives an edge bend stiffness that is proportional to \(\text{surfaceBendStiffness} \cdot \text{surfaceThickness}^{3}\).

  • PhysxSchema.PhysxMaterialAPI frictionCombineMode is supported for rigid-deformable contacts only, but not for deformable-deformable contacts. Applying a PhysxSchema.PhysxMaterialAPI to a deformable material has no effect.

Attachments and Collision Filter Limitations#

  • The low-Level attachment type OmniPhysicsTriTriAttachment is not supported.

  • Collision filtering via OmniPhysicsElementCollisionFilter is currently not supported between two surface deformable bodies.

  • Attachment stiffness and damping are not supported.

  • Autogenerated attachments via PhysxAutoDeformableAttachmentAPI are limited as follows:

    • Attachments between two surface deformables are not supported.

    • Rigid surface sampling is currently not supported. The attributes enableRigidSurfaceAttachments, rigidSurfaceSamplingDistance are ignored.

    • Attachment mask shapes are restricted to UsdGeom.Sphere, UsdGeom.Box and UsdGeom.Capsule prims. Other shape types are ignored.

Render Mesh Skinning Limitations#

  • PhysxAutoDeformableMeshSimplificationAPI, physxDeformableBody:forceConforming has no effect when used on surface deformables.

  • PhysxAutoDeformableMeshSimplificationAPI, physxDeformableBody:remeshingEnabled should be set to False when used on surface deformables, unless the source mesh (specified with PhysxAutoDeformableBodyAPI physxDeformableBody:cookingSourceMesh) is closed. Otherwise a double layered simulation mesh is created, which doesn’t work well with skinning.

  • Skinning on surface deformables is expected to not work well with non-manifold simulation meshes.