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

Fix some bugs in cloth generation #339

Merged
merged 3 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 36 additions & 20 deletions omnigibson/systems/micro_particle_system.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -43,6 +44,7 @@

# TODO: Tune these default values!
# TODO (eric): figure out whether one offset can fit all
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
Expand Down Expand Up @@ -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:
cgokmen marked this conversation as resolved.
Show resolved Hide resolved
# 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 *= np.sqrt(2) # halve the number of vertices
else:
break
else:
raise ValueError(f"Could not remesh with less than MAX_CLOTH_PARTICLES ({m.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))

Expand Down
2 changes: 1 addition & 1 deletion omnigibson/utils/usd_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading