From 9d3012cf1efba7d0c6628c286fa486bfc6528e63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cem=20G=C3=B6kmen?= <1408354+cgokmen@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:28:22 -0800 Subject: [PATCH 1/2] Switch to ClothPrimView and fix some bugs in cloth generation Revert "Switch to ClothPrimView and fix some bugs in cloth generation" This reverts commit a4f865bdac49db2a01ee062feadc87bb0d1ce6b5. Revert "Revert "Switch to ClothPrimView and fix some bugs in cloth generation"" This reverts commit ed5d8b9b72a8fdb6766ce98e3141adaeb79fb991. --- omnigibson/systems/micro_particle_system.py | 56 +++++++++++++-------- omnigibson/utils/usd_utils.py | 2 +- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/omnigibson/systems/micro_particle_system.py b/omnigibson/systems/micro_particle_system.py index 0c0361f29..c07233e02 100644 --- a/omnigibson/systems/micro_particle_system.py +++ b/omnigibson/systems/micro_particle_system.py @@ -1,3 +1,4 @@ +import uuid import omnigibson as og from omnigibson.macros import gm, create_module_macros from omnigibson.prims.prim_base import BasePrim @@ -43,6 +44,7 @@ # TODO: Tune these default values! # TODO (eric): figure out whether one offset can fit all +m.MAX_CLOTH_PARTICLES = 20000 m.CLOTH_PARTICLE_CONTACT_OFFSET = 0.0075 m.CLOTH_REMESHING_ERROR_THRESHOLD = 0.05 m.CLOTH_STRETCH_STIFFNESS = 10000.0 @@ -1540,44 +1542,58 @@ def clothify_mesh_prim(cls, mesh_prim, remesh=True, particle_distance=None): # we convert our mesh into a trimesh mesh, then export it to a temp file, then load it into pymeshlab tm = mesh_prim_to_trimesh_mesh(mesh_prim=mesh_prim, include_normals=True, include_texcoord=True) # Tmp file written to: {tmp_dir}/{tmp_fname}/{tmp_fname}.obj - tmp_name = f"{mesh_prim.GetName()}_{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}" + tmp_name = str(uuid.uuid4()) tmp_dir = os.path.join(tempfile.gettempdir(), tmp_name) tmp_fpath = os.path.join(tmp_dir, f"{tmp_name}.obj") Path(tmp_dir).mkdir(parents=True, exist_ok=True) tm.export(tmp_fpath) - ms = pymeshlab.MeshSet() - ms.load_new_mesh(tmp_fpath) - # Re-mesh based on @particle_distance - distance chosen such that at rest particles should be just touching - # each other. The 1.5 magic number comes from the particle cloth demo from omni - # Note that this means that the particles will overlap with each other, since at dist = 2 * contact_offset - # the particles are just touching each other at rest + # Start with the default particle distance particle_distance = cls.particle_contact_offset * 2 / (1.5 * np.mean(mesh_prim.GetAttribute("xformOp:scale").Get())) \ if particle_distance is None else particle_distance - avg_edge_percentage_mismatch = 1.0 - iters = 0 - # Loop re-meshing until average edge percentage is within error threshold or we reach the max number of tries - while avg_edge_percentage_mismatch > m.CLOTH_REMESHING_ERROR_THRESHOLD: - ms.meshing_isotropic_explicit_remeshing(iterations=5, targetlen=pymeshlab.AbsoluteValue(particle_distance)) - avg_edge_percentage_mismatch = abs(1.0 - particle_distance / ms.get_geometric_measures()["avg_edge_length"]) - iters += 1 - if iters > 5: + + # Repetitively re-mesh at lower resolution until we have a mesh that has less than MAX_CLOTH_PARTICLES vertices + for _ in range(3): + ms = pymeshlab.MeshSet() + ms.load_new_mesh(tmp_fpath) + + # Re-mesh based on @particle_distance - distance chosen such that at rest particles should be just touching + # each other. The 1.5 magic number comes from the particle cloth demo from omni + # Note that this means that the particles will overlap with each other, since at dist = 2 * contact_offset + # the particles are just touching each other at rest + + avg_edge_percentage_mismatch = 1.0 + # Loop re-meshing until average edge percentage is within error threshold or we reach the max number of tries + for _ in range(5): + if avg_edge_percentage_mismatch <= m.CLOTH_REMESHING_ERROR_THRESHOLD: + break + + ms.meshing_isotropic_explicit_remeshing(iterations=5, targetlen=pymeshlab.AbsoluteValue(particle_distance)) + avg_edge_percentage_mismatch = abs(1.0 - particle_distance / ms.get_geometric_measures()["avg_edge_length"]) + else: # Terminate anyways, but don't fail log.warn("The generated cloth may not have evenly distributed particles.") + + # Check if we have too many vertices + cm = ms.current_mesh() + if cm.vertex_number() > m.MAX_CLOTH_PARTICLES: + # We have too many vertices, so we will re-mesh again + particle_distance *= 1.47 # halve the number of vertices + else: break + else: + raise ValueError("Could not remesh with less than MAX_CLOTH_PARTICLES vertices!") # Re-write data to @mesh_prim - cm = ms.current_mesh() new_face_vertex_ids = cm.face_matrix().flatten() new_texcoord = cm.wedge_tex_coord_matrix() - new_vertices = cm.vertex_matrix()[new_face_vertex_ids] - new_normals = cm.vertex_normal_matrix()[new_face_vertex_ids] - n_vertices = len(new_vertices) + new_vertices = cm.vertex_matrix() + new_normals = cm.vertex_normal_matrix() n_faces = len(cm.face_matrix()) mesh_prim.GetAttribute("faceVertexCounts").Set(np.ones(n_faces, dtype=int) * 3) mesh_prim.GetAttribute("points").Set(Vt.Vec3fArray.FromNumpy(new_vertices)) - mesh_prim.GetAttribute("faceVertexIndices").Set(np.arange(n_vertices)) + mesh_prim.GetAttribute("faceVertexIndices").Set(new_face_vertex_ids) mesh_prim.GetAttribute("normals").Set(Vt.Vec3fArray.FromNumpy(new_normals)) mesh_prim.GetAttribute("primvars:st").Set(Vt.Vec2fArray.FromNumpy(new_texcoord)) diff --git a/omnigibson/utils/usd_utils.py b/omnigibson/utils/usd_utils.py index f8fe10c8a..2bced0c3d 100644 --- a/omnigibson/utils/usd_utils.py +++ b/omnigibson/utils/usd_utils.py @@ -768,7 +768,7 @@ def sample_mesh_keypoints(mesh_prim, n_keypoints, n_keyfaces, seed=None): n_vertices = len(faces_flat) # Sample vertices - unique_vertices = np.unique(faces_flat, return_index=True)[1] + unique_vertices = np.unique(faces_flat) assert len(unique_vertices) == n_unique_vertices keypoint_idx = np.random.choice(unique_vertices, size=n_keypoints, replace=False) if \ n_unique_vertices > n_keypoints else unique_vertices From af228de4931f56f84b8b66537e705c7043909de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cem=20G=C3=B6kmen?= <1408354+cgokmen@users.noreply.github.com> Date: Tue, 12 Dec 2023 16:10:25 -0800 Subject: [PATCH 2/2] Address comments. --- omnigibson/systems/micro_particle_system.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/omnigibson/systems/micro_particle_system.py b/omnigibson/systems/micro_particle_system.py index c07233e02..e6e44b441 100644 --- a/omnigibson/systems/micro_particle_system.py +++ b/omnigibson/systems/micro_particle_system.py @@ -44,7 +44,7 @@ # TODO: Tune these default values! # TODO (eric): figure out whether one offset can fit all -m.MAX_CLOTH_PARTICLES = 20000 +m.MAX_CLOTH_PARTICLES = 20000 # Comes from a limitation in physx - do not increase m.CLOTH_PARTICLE_CONTACT_OFFSET = 0.0075 m.CLOTH_REMESHING_ERROR_THRESHOLD = 0.05 m.CLOTH_STRETCH_STIFFNESS = 10000.0 @@ -1578,11 +1578,11 @@ def clothify_mesh_prim(cls, mesh_prim, remesh=True, particle_distance=None): cm = ms.current_mesh() if cm.vertex_number() > m.MAX_CLOTH_PARTICLES: # We have too many vertices, so we will re-mesh again - particle_distance *= 1.47 # halve the number of vertices + particle_distance *= np.sqrt(2) # halve the number of vertices else: break else: - raise ValueError("Could not remesh with less than MAX_CLOTH_PARTICLES vertices!") + raise ValueError(f"Could not remesh with less than MAX_CLOTH_PARTICLES ({m.MAX_CLOTH_PARTICLES}) vertices!") # Re-write data to @mesh_prim new_face_vertex_ids = cm.face_matrix().flatten()