# Copyright (c) 2022 NVIDIA CORPORATION. All rights reserved. # NVIDIA CORPORATION and its licensors retain all intellectual property # and proprietary rights in and to this software, related documentation # and any modifications thereto. Any use, reproduction, disclosure or # distribution of this software and related documentation without an express # license agreement from NVIDIA CORPORATION is strictly prohibited. ########################################################################### # Example Sim Cloth # # Shows a simulation of an FEM cloth model colliding against a static # rigid body mesh using the wp.sim.ModelBuilder(). # ########################################################################### import argparse import math import os from enum import Enum import numpy as np from pxr import Usd, UsdGeom import warp as wp import warp.sim import warp.sim.render wp.init() class IntegratorType(Enum): EULER = "euler" XPBD = "xpbd" def __str__(self): return self.value class Example: def __init__(self, stage, integrator=IntegratorType.EULER): self.device = wp.get_device() self.integrator_type = integrator self.sim_width = 64 self.sim_height = 32 self.sim_fps = 60.0 self.sim_substeps = 32 self.sim_duration = 5.0 self.sim_frames = int(self.sim_duration * self.sim_fps) self.frame_dt = 1.0 / self.sim_fps self.sim_dt = self.frame_dt / self.sim_substeps self.sim_time = 0.0 self.sim_use_graph = wp.get_device().is_cuda self.profiler = {} builder = wp.sim.ModelBuilder() if self.integrator_type == IntegratorType.EULER: builder.add_cloth_grid( pos=wp.vec3(0.0, 4.0, 0.0), rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), math.pi * 0.5), vel=wp.vec3(0.0, 0.0, 0.0), dim_x=self.sim_width, dim_y=self.sim_height, cell_x=0.1, cell_y=0.1, mass=0.1, fix_left=True, tri_ke=1.0e3, tri_ka=1.0e3, tri_kd=1.0e1, ) else: builder.add_cloth_grid( pos=wp.vec3(0.0, 4.0, 0.0), rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), math.pi * 0.5), vel=wp.vec3(0.0, 0.0, 0.0), dim_x=self.sim_width, dim_y=self.sim_height, cell_x=0.1, cell_y=0.1, mass=0.1, fix_left=True, edge_ke=1.0e2, add_springs=True, spring_ke=1.0e3, spring_kd=0.0, ) usd_stage = Usd.Stage.Open(os.path.join(os.path.dirname(__file__), "assets/bunny.usd")) usd_geom = UsdGeom.Mesh(usd_stage.GetPrimAtPath("/bunny/bunny")) mesh_points = np.array(usd_geom.GetPointsAttr().Get()) mesh_indices = np.array(usd_geom.GetFaceVertexIndicesAttr().Get()) mesh = wp.sim.Mesh(mesh_points, mesh_indices) builder.add_shape_mesh( body=-1, mesh=mesh, pos=wp.vec3(1.0, 0.0, 1.0), rot=wp.quat_from_axis_angle(wp.vec3(0.0, 1.0, 0.0), math.pi * 0.5), scale=wp.vec3(2.0, 2.0, 2.0), ke=1.0e2, kd=1.0e2, kf=1.0e1, ) self.model = builder.finalize() self.model.ground = True self.model.soft_contact_ke = 1.0e4 self.model.soft_contact_kd = 1.0e2 if self.integrator_type == IntegratorType.EULER: self.integrator = wp.sim.SemiImplicitIntegrator() else: self.integrator = wp.sim.XPBDIntegrator(iterations=1) self.state_0 = self.model.state() self.state_1 = self.model.state() self.renderer = wp.sim.render.SimRenderer(self.model, stage, scaling=40.0) self.graph = None if self.sim_use_graph: # create update graph wp.capture_begin(self.device) try: self.update() finally: self.graph = wp.capture_end(self.device) def update(self): with wp.ScopedTimer("simulate", dict=self.profiler): if self.sim_use_graph and self.graph: wp.capture_launch(self.graph) self.sim_time += self.frame_dt else: wp.sim.collide(self.model, self.state_0) for _ in range(self.sim_substeps): self.state_0.clear_forces() self.integrator.simulate(self.model, self.state_0, self.state_1, self.sim_dt) if not wp.get_device().is_capturing: self.sim_time += self.sim_dt # swap states (self.state_0, self.state_1) = (self.state_1, self.state_0) def render(self, is_live=False): with wp.ScopedTimer("render", active=True): time = 0.0 if is_live else self.sim_time self.renderer.begin_frame(time) self.renderer.render(self.state_0) self.renderer.end_frame() if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument( "--integrator", help="Type of integrator", type=IntegratorType, choices=list(IntegratorType), default=IntegratorType.EULER, ) args = parser.parse_args() stage_path = os.path.join(os.path.dirname(__file__), "outputs/example_sim_cloth.usd") example = Example(stage_path, integrator=args.integrator) for i in range(example.sim_frames): example.update() example.render() frame_times = example.profiler["simulate"] print("\nAverage frame sim time: {:.2f} ms".format(sum(frame_times) / len(frame_times))) example.renderer.save()