Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[QUESTION] About garment simulation #433

Open
MontaEllis opened this issue Jan 15, 2025 · 2 comments
Open

[QUESTION] About garment simulation #433

MontaEllis opened this issue Jan 15, 2025 · 2 comments
Labels
question The issue author requires information

Comments

@MontaEllis
Copy link

I can achieve convincing results with the following simulation code.

@MontaEllis MontaEllis added the question The issue author requires information label Jan 15, 2025
@MontaEllis
Copy link
Author

# 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 math
import os
from enum import Enum
import igl
import numpy as np
from pxr import Usd, UsdGeom

import warp as wp
import warp.examples
import warp.sim
import warp.sim.render
from pytorch3d.io import load_obj

class IntegratorType(Enum):
    EULER = "euler"
    XPBD = "xpbd"
    VBD = "vbd"

    def __str__(self):
        return self.value


class Example:
    def __init__(
        self, stage_path="example_cloth.usd", integrator: IntegratorType = IntegratorType.EULER, height=32, width=64
    ):
        self.integrator_type = integrator

        self.sim_height = height
        self.sim_width = width

        fps = 60
        self.sim_substeps = 10
        self.frame_dt = 1.0 / fps
        self.sim_dt = self.frame_dt / self.sim_substeps
        self.sim_time = 0.0
        self.profiler = {}

        builder = wp.sim.ModelBuilder()


        warped_vertices_final_origin, faces_list, _ = load_obj('tt.obj')
        cloth_vertices = warped_vertices_final_origin.detach().cpu().numpy()
        cloth_faces = faces_list[0].detach().cpu().numpy()
        self.cloth_faces = cloth_faces
        cloth_indices = cloth_faces.flatten()

        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,
            # )
            builder.add_cloth_mesh(
                pos=(0.0, 0.0, 0.0),
                rot=wp.quat_from_axis_angle(wp.vec3(0.0, 1.0, 0.0), wp.degrees(0.0)),
                scale=1.0,
                vel=(0.0, 0.0, 0.0),
                vertices=cloth_vertices,
                indices=cloth_indices,
                density=1.0,
                edge_ke=1.0,
                edge_kd=10.0,
                tri_ke=1.0e5,
                tri_ka=1.0,
                tri_kd=1.0e5,
                tri_drag=0.0,
                tri_lift=0.0,
                add_springs=True,
                spring_ke=1.0e3,
                spring_kd=0.0,
            )
        elif self.integrator_type == IntegratorType.XPBD:
            # 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,
            # )
            builder.add_cloth_mesh(
                pos=wp.vec3(0.0, 0.0, 0.0),
                rot=wp.quat_from_axis_angle(wp.vec3(0.0, 1.0, 0.0), wp.degrees(0.0)),
                scale=1.0,
                vel=(0.0, 0.0, 0.0),
                vertices=cloth_vertices,
                indices=cloth_indices,
                density=1.0,
                edge_ke=1.0,
                edge_kd=10.0,
                tri_ke=1.0e5,
                tri_ka=1.0,
                tri_kd=1.0e5,
                tri_drag=0.0,
                tri_lift=0.0,
                add_springs=True,
                spring_ke=1.0e3,
                spring_kd=0.0,
            )
        else:
            # VBD
            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=1e4,
                tri_ka=1e4,
                tri_kd=1e-5,
                edge_ke=100,
            )

        body_vertices, body_indices, body_faces = self.load_obj('body.obj')
        self.body_indices = body_indices
        # usd_stage = Usd.Stage.Open(os.path.join(warp.examples.get_asset_directory(), "bunny.usd"))
        # usd_geom = UsdGeom.Mesh(usd_stage.GetPrimAtPath("/root/bunny"))

        # mesh_points = np.array(usd_geom.GetPointsAttr().Get())
        # mesh_indices = np.array(usd_geom.GetFaceVertexIndicesAttr().Get())

        mesh = wp.sim.Mesh(body_vertices*100.0, body_indices)

        builder.add_shape_mesh(
            body=-1,
            mesh=mesh,
            pos=wp.vec3(0.0, 0.0, 0.0),
            rot=wp.quat_from_axis_angle(wp.vec3(0.0, 1.0, 0.0), wp.degrees(0.0)),
            scale=wp.vec3(1.0, 1.0, 1.0),
            ke=1.0e2,
            kd=1.0e2,
            kf=1.0e1,
        )

        if self.integrator_type == IntegratorType.VBD:
            builder.color()

        self.model = builder.finalize()
        self.model.ground = False
        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()
        elif self.integrator_type == IntegratorType.XPBD:
            self.integrator = wp.sim.XPBDIntegrator(iterations=1)
        else:
            self.integrator = wp.sim.VBDIntegrator(self.model, iterations=1)

        self.state_0 = self.model.state()
        self.state_1 = self.model.state()

        if stage_path:
            self.renderer = wp.sim.render.SimRenderer(self.model, stage_path, scaling=40.0)
        else:
            self.renderer = None

        self.use_cuda_graph = wp.get_device().is_cuda
        if self.use_cuda_graph:
            with wp.ScopedCapture() as capture:
                self.simulate()
            self.graph = capture.graph

    def simulate(self):
        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)

            # swap states
            (self.state_0, self.state_1) = (self.state_1, self.state_0)

    def load_obj(self, path):
        v, f = igl.read_triangle_mesh(str(path))
        return v, f.flatten(), f
    
    def step(self):
        with wp.ScopedTimer("step", dict=self.profiler):
            if self.use_cuda_graph:
                wp.capture_launch(self.graph)
            else:
                self.simulate()
        self.sim_time += self.frame_dt

    def render(self):
        if self.renderer is None:
            return

        with wp.ScopedTimer("render"):
            self.renderer.begin_frame(self.sim_time)
            self.renderer.render(self.state_0)
            self.renderer.end_frame()

def save_obj2(
        output_file_path, 
        vertices, faces_with_texture, uv_list, 
        vert_normals=None, mtl_file_name=None, mat_name=None):
    """Save an obj file with a texture information (if provided)"""

    with open(output_file_path, 'w') as f:
        if mtl_file_name is not None:
            f.write(f'mtllib {mtl_file_name}\n')

        for v in vertices:
            f.write(f"v {v[0]} {v[1]} {v[2]}\n")

        # for vt in uv_list:
        #     f.write(f"vt {vt[0]} {vt[1]}\n")

        if vert_normals is not None:
            for vn in vert_normals:
                f.write(f"vn {vn[0]} {vn[1]} {vn[2]}\n")
            
        f.write('s 1\n')
        if mtl_file_name is not None:
            f.write(f'usemtl {mat_name}\n')

        # if vert_normals is not None:
        #     for v_id0, tex_id0, v_id1, tex_id1, v_id2, tex_id2, in faces_with_texture:
        #         f.write(f"f {v_id0 + 1}/{tex_id0 + 1}/{v_id0 + 1} "
        #                 f"{v_id1 + 1}/{tex_id1 + 1}/{v_id1 + 1} "
        #                 f"{v_id2 + 1}/{tex_id2 + 1}/{v_id2 + 1}\n")
        # else:
        #     for v_id0, tex_id0, v_id1, tex_id1, v_id2, tex_id2, in faces_with_texture :
        #         f.write(f"f {v_id0 + 1}/{tex_id0 + 1} "
        #                 f"{v_id1 + 1}/{tex_id1 + 1} "
        #                 f"{v_id2 + 1}/{tex_id2 + 1}\n")
                
        for v_id0, v_id1, v_id2, in faces_with_texture :
            f.write(f"f {v_id0 + 1} "
                    f"{v_id1 + 1} "
                    f"{v_id2 + 1}\n")
            
if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("--device", type=str, default=None, help="Override the default Warp device.")
    parser.add_argument(
        "--stage_path",
        type=lambda x: None if x == "None" else str(x),
        default="example_cloth.usd",
        help="Path to the output USD file.",
    )
    parser.add_argument("--num_frames", type=int, default=30, help="Total number of frames.")
    parser.add_argument(
        "--integrator",
        help="Type of integrator",
        type=IntegratorType,
        choices=list(IntegratorType),
        default=IntegratorType.EULER,
    )
    parser.add_argument("--width", type=int, default=64, help="Cloth resolution in x.")
    parser.add_argument("--height", type=int, default=32, help="Cloth resolution in y.")

    args = parser.parse_known_args()[0]

    with wp.ScopedDevice(args.device):
        example = Example(stage_path=args.stage_path, integrator=args.integrator, height=args.height, width=args.width)

        for _i in range(args.num_frames):
            example.step()
            example.render()

        save_obj2('sim.obj',vertices=wp.to_torch(example.state_0.particle_q), faces_with_texture=np.array(example.cloth_faces), uv_list=None)

        frame_times = example.profiler["step"]
        print("\nAverage frame sim time: {:.2f} ms".format(sum(frame_times) / len(frame_times)))

        if example.renderer:
            example.renderer.save()

@MontaEllis
Copy link
Author

there is the mesh I used.

Downloads.zip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question The issue author requires information
Projects
None yet
Development

No branches or pull requests

1 participant