Independent position control of particle for cloth in physx preview

I would like to control position of single particle of cloth element in Isaac Sim. I am using physx preview extension and would like to control particle position of cloth with python API like shift+left click and drug as in the video below.


This feature is now available or future release? That is my question.

This is my code that is creating particle cloth.

from curses import erasechar
from dis import dis
from socket import gaierror
import omni
from locale import currency
from tokenize import Triple
from construct import Tunnel
import numpy as np
import math
import time
import copy

from omni.isaac.kit import SimulationApp

simulation_app = SimulationApp({"headless": False})
from pxr import Usd, UsdGeom, Sdf, Gf, Vt, PhysxSchema, UsdPhysics


def addPhysxParticleSystem(
    stage,
    particle_system_path,
    contact_offset,
    rest_offset,
    particle_contact_offset,
    solid_rest_offset,
    fluid_rest_offset,
    solver_position_iterations,
    solver_velocity_iterations,
    wind,
    scenePath,
):

    particle_system = PhysxSchema.PhysxParticleSystem.Define(stage, particle_system_path)
    if particle_system:
        particle_system.CreateContactOffsetAttr().Set(contact_offset)
        particle_system.CreateRestOffsetAttr().Set(rest_offset)
        particle_system.CreateParticleContactOffsetAttr().Set(particle_contact_offset)
        particle_system.CreateSolidRestOffsetAttr().Set(solid_rest_offset)
        particle_system.CreateFluidRestOffsetAttr().Set(fluid_rest_offset)
        particle_system.CreateSolverPositionIterationCountAttr().Set(solver_position_iterations)
        particle_system.CreateSolverVelocityIterationCountAttr().Set(solver_velocity_iterations)
        particle_system.CreateWindAttr().Set(wind)

        # Reference simulation owner using PhysxPhysicsAPI
        physics_api = PhysxSchema.PhysxPhysicsAPI.Apply(particle_system.GetPrim())
        physics_api.CreateSimulationOwnerRel().SetTargets([scenePath])
        
        return particle_system
    else:
        return None

def addSpringConstraint(springConnections, i, j, springStiffnesses, stiffness, springDampings, damping, springRestLengths, distance):
    springConnections.append(Gf.Vec2i(i, j))
    springStiffnesses.append(stiffness)
    springDampings.append(damping)
    springRestLengths.append(distance.GetLength())

def gridIndex(x, y, dx):
    return y * dx + x

def CreateSpringGrid(
    lower,
    dx,
    dy,
    radius,
    positions,
    normals,
    restPositions,
    velocities,
    invMasses,
    triangleIndices,
    springConnections,
    springStiffnesses,
    springDampings,
    springRestLengths,
    stretchStiffness,
    stretchDamping,
    bendStiffness,
    bendDamping,
    shearStiffness,
    shearDamping,
    invMass,
    staticEdges=False,
):

    for y in range(dy):
        for x in range(dx):
            pos_offset = Gf.Vec3f(radius * x, radius * y, radius * 1.0)
            positions.append(lower + pos_offset)
            normals.append(Gf.Vec3f(0.0, 0.0, 1.0))
            restPositions.append(lower + pos_offset)
            velocities.append(Gf.Vec3f(0.0, 0.0, 0.0))
            if staticEdges is True:
                if x == 0 or x == (dx - 1):
                    invMasses.append(0.0)
                else:
                    invMasses.append(invMass)
            else:
                invMasses.append(invMass)

            if x > 0 and y > 0:
                triangleIndices.append(gridIndex(x - 1, y - 1, dx))
                triangleIndices.append(gridIndex(x, y - 1, dx))
                triangleIndices.append(gridIndex(x, y, dx))
                triangleIndices.append(gridIndex(x - 1, y - 1, dx))
                triangleIndices.append(gridIndex(x, y, dx))
                triangleIndices.append(gridIndex(x - 1, y, dx))

    # horizontal
    for y in range(dy):
        for x in range(dx):
            i = y * dx + x

            if x > 0:
                j = y * dx + x - 1
                addSpringConstraint(
                    springConnections,
                    i,
                    j,
                    springStiffnesses,
                    stretchStiffness,
                    springDampings,
                    stretchDamping,
                    springRestLengths,
                    positions[i] - positions[j],
                )

            if x > 1 and bendStiffness > 0.0:
                j = y * dx + x - 2
                addSpringConstraint(
                    springConnections,
                    i,
                    j,
                    springStiffnesses,
                    bendStiffness,
                    springDampings,
                    bendDamping,
                    springRestLengths,
                    positions[i] - positions[j],
                )

            if y > 0 and x < dx - 1 and shearStiffness > 0.0:
                j = (y - 1) * dx + x + 1
                addSpringConstraint(
                    springConnections,
                    i,
                    j,
                    springStiffnesses,
                    shearStiffness,
                    springDampings,
                    shearDamping,
                    springRestLengths,
                    positions[i] - positions[j],
                )

            if y > 0 and x > 0 and shearStiffness > 0.0:
                j = (y - 1) * dx + x - 1
                addSpringConstraint(
                    springConnections,
                    i,
                    j,
                    springStiffnesses,
                    shearStiffness,
                    springDampings,
                    shearDamping,
                    springRestLengths,
                    positions[i] - positions[j],
                )

def addPhysxClothWithConstraints(
    stage,
    path,
    positions,
    normals,
    rest_positions,
    velocities,
    inv_masses,
    triangle_indices,
    spring_connections,
    spring_stiffnesses,
    spring_dampings,
    spring_rest_lengths,
    self_collision,
    self_collision_filter,
    inv_gravity,
    particle_group,
    particle_system_path,
):

    mesh = UsdGeom.Mesh.Define(stage, path)
    prim = mesh.GetPrim()
    mesh.CreateDoubleSidedAttr().Set(True)

    vertex_count_attr = mesh.CreateFaceVertexCountsAttr()
    vertex_indices_attr = mesh.CreateFaceVertexIndicesAttr()
    norm_attr = mesh.CreateNormalsAttr()
    point_attr = mesh.CreatePointsAttr()

    # Triangle array's vertex count per face is always 3
    vertex_count = 3
    array_size = int(len(triangle_indices) / 3)
    index_array = Vt.IntArray(array_size, vertex_count)
    vertex_count_attr.Set(index_array)

    vertex_indices_attr.Set(triangle_indices)
    norm_attr.Set(normals)
    point_attr.Set(positions)

    cloth_api = PhysxSchema.PhysxClothAPI.Apply(prim)
    
    cloth_api.CreateSelfCollisionAttr().Set(self_collision)
    cloth_api.CreateSelfCollisionFilterAttr().Set(self_collision_filter)
    cloth_api.CreateParticleGroupAttr().Set(particle_group)

    # Reference simulation owner using PhysxPhysicsAPI
    physics_api = PhysxSchema.PhysxPhysicsAPI.Apply(prim)
    physics_api.CreateSimulationOwnerRel().SetTargets([particle_system_path])
    # Custom attributes
    prim.CreateAttribute("invGravity", Sdf.ValueTypeNames.Bool).Set(inv_gravity)

    prim.CreateAttribute("springConnections", Sdf.ValueTypeNames.Int2Array).Set(spring_connections)
    prim.CreateAttribute("springStiffnesses", Sdf.ValueTypeNames.FloatArray).Set(spring_stiffnesses)
    prim.CreateAttribute("springDampings", Sdf.ValueTypeNames.FloatArray).Set(spring_dampings)
    prim.CreateAttribute("springRestLengths", Sdf.ValueTypeNames.FloatArray).Set(spring_rest_lengths)

    prim.CreateAttribute("restPositions", Sdf.ValueTypeNames.Point3fArray).Set(rest_positions)
    prim.CreateAttribute("velocities", Sdf.ValueTypeNames.Point3fArray).Set(velocities)

    prim.CreateAttribute("inverseMasses", Sdf.ValueTypeNames.FloatArray).Set(inv_masses)

    return point_attr

from omni.isaac.core import World
_my_world = World()
_my_world.scene.add_default_ground_plane()
_stage = _my_world.scene.stage

from omni.isaac.core import PhysicsContext
physics_context = _my_world.get_physics_context()
physics_context.enable_gpu_dynamics(True)
physics_context.enable_ccd(False)
physics_context.set_solver_type("TGS")
physics_context.enable_stablization(False)


from omni.isaac.core.utils.extensions import enable_extension

enable_extension("omni.physx.preview")

dimX = 16
dimY = 16
radius = 1.5
particleSystemPath = Sdf.Path("/particleSystem0")
restOffset = radius 
contactOffset = radius + 0.1 
defaultPrimPath = str(_stage.GetDefaultPrim().GetPath())
scene = UsdPhysics.Scene.Define(_stage, defaultPrimPath + "/physicsScene")
addPhysxParticleSystem(
    _stage,
    particleSystemPath,
    contactOffset,
    restOffset,
    contactOffset,
    restOffset,
    0.0,
    16,
    1,
    Gf.Vec3f(0, 0, 0),
    scene.GetPath(),
)

clothPath = Sdf.Path("/cloth0")
positions_list = []
normals_list = []
restPositions_list = []
velocities_list = []
invMasses_list = []
triangleIndices_list = []
springConnections_list = []
springStiffnesses_list = []
springDampings_list = []
springRestLengths_list = []
lower = Gf.Vec3f(-radius * dimX / 2, -radius * dimY / 2, 0.0)
stretchStiffness = 500.0
bendStiffness = 300.0
shearStiffness = 300.0
damping = 20.0
invMass = 400.0
CreateSpringGrid(
    lower,
    dimX,
    dimY,
    radius,
    positions_list,
    normals_list,
    restPositions_list,
    velocities_list,
    invMasses_list,
    triangleIndices_list,
    springConnections_list,
    springStiffnesses_list,
    springDampings_list,
    springRestLengths_list,
    stretchStiffness,
    damping,
    bendStiffness,
    damping,
    shearStiffness,
    damping,
    invMass,
    False,
)

invMasses_list[0] = 0.0
invMasses_list[240] = 0.0

positions = Vt.Vec3fArray(positions_list)
normals = Vt.Vec3fArray(normals_list)
restPositions = Vt.Vec3fArray(restPositions_list)
velocities = Vt.Vec3fArray(velocities_list)
invMasses = Vt.FloatArray(invMasses_list)
triangleIndices = Vt.IntArray(triangleIndices_list)
springConnections = Vt.Vec2iArray(springConnections_list)
springStiffnesses = Vt.FloatArray(springStiffnesses_list)
springDampings = Vt.FloatArray(springDampings_list)
springRestLengths = Vt.FloatArray(springRestLengths_list)
point_attr = addPhysxClothWithConstraints(
    _stage,
    clothPath,
    positions,
    normals,
    restPositions,
    velocities,
    invMasses,
    triangleIndices,
    springConnections,
    springStiffnesses,
    springDampings,
    springRestLengths,
    True,
    True,
    False,
    0,
    particleSystemPath,
)

tissue_color = Vt.Vec3fArray([Gf.Vec3f(71.0 / 255.0, 165.0 / 255.0, 1.0)])
tissue_prim = UsdGeom.Mesh.Define(_stage, Sdf.Path("/tissue"))
tissue_prim.CreateDisplayColorAttr(tissue_color)

translation = Gf.Vec3f(0.0, 0.0, 0.0)
tissue_prim.AddTranslateOp().Set(translation)

from omni.isaac.core.utils.viewports import set_camera_view
from pxr import UsdLux
from omni.isaac.synthetic_utils import SyntheticDataHelper
import os
set_camera_view(eye=np.array([53.0, 0.0, 30.0]), target=np.array([0.0, 0.0, 0.0]))
light_prim = UsdLux.DistantLight.Define(_my_world.scene.stage, Sdf.Path("/DistantLight"))
light_prim.CreateIntensityAttr(500)
        
_my_world.reset()

while True:
    _my_world.step(render=True)
    # points = np.array(point_attr.Get())
    # points[255][2] = 5.0
    # point_attr.Set(points)

Thank you.
Sugita

1 Like