Skip to content

Commit

Permalink
Adjustable vertex-triangle contact radius in SemiImplicitIntegrator
Browse files Browse the repository at this point in the history
Fixes GH-329
AnkaChan authored and shi-eric committed Nov 26, 2024
1 parent 6741b68 commit eacad4f
Showing 4 changed files with 123 additions and 3 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -30,6 +30,11 @@
providing conforming discretization of `curl` and `div` operators, respectively.
- warp.sim: Added a graph coloring module that supports converting trimesh into a vertex graph and applying coloring.
The `wp.sim.ModelBuilder` now includes methods to color particles for use with `wp.sim.VBDIntegrator()`, users should call `builder.color()` before finalizing assets.
- warp.sim: Add support for a per-particle radius for soft-body triangle contact using the `wp.sim.Model.particle_radius`
array ([docs](https://nvidia.github.io/warp/modules/sim.html#warp.sim.Model.particle_radius)), replacing the previous
hard-coded value of 0.01 ([GH-329](https://github.com/NVIDIA/warp/issues/329)).
- Add a `particle_radius` parameter to `wp.sim.ModelBuilder.add_cloth_mesh()` and `wp.sim.ModelBuilder.add_cloth_grid()`
to set a uniform radius for the added particles.

### Changed

4 changes: 3 additions & 1 deletion warp/sim/integrator_euler.py
Original file line number Diff line number Diff line change
@@ -264,6 +264,7 @@ def eval_triangles_contact(
v: wp.array(dtype=wp.vec3),
indices: wp.array2d(dtype=int),
materials: wp.array2d(dtype=float),
particle_radius: wp.array(dtype=float),
f: wp.array(dtype=wp.vec3),
):
tid = wp.tid()
@@ -303,7 +304,7 @@ def eval_triangles_contact(
diff = pos - closest
dist = wp.dot(diff, diff)
n = wp.normalize(diff)
c = wp.min(dist - 0.01, 0.0) # 0 unless within 0.01 of surface
c = wp.min(dist - particle_radius[particle_no], 0.0) # 0 unless within particle's contact radius
# c = wp.leaky_min(dot(n, x0)-0.01, 0.0, 0.0)
fn = n * c * 1e5

@@ -1697,6 +1698,7 @@ def eval_triangle_contact_forces(model: Model, state: State, particle_f: wp.arra
state.particle_qd,
model.tri_indices,
model.tri_materials,
model.particle_radius,
],
outputs=[particle_f],
device=model.device,
7 changes: 5 additions & 2 deletions warp/sim/model.py
Original file line number Diff line number Diff line change
@@ -3841,6 +3841,7 @@ def add_cloth_grid(
add_springs: bool = False,
spring_ke: float = default_spring_ke,
spring_kd: float = default_spring_kd,
particle_radius: float = default_particle_radius,
):
"""Helper to create a regular planar cloth grid
@@ -3890,7 +3891,7 @@ def grid_index(x, y, dim_x):
m = 0.0
particle_flag = wp.uint32(int(particle_flag) & ~int(PARTICLE_FLAG_ACTIVE))

self.add_particle(p, vel, m, flags=particle_flag)
self.add_particle(p, vel, m, flags=particle_flag, radius=particle_radius)

if x > 0 and y > 0:
if reverse_winding:
@@ -3974,6 +3975,7 @@ def add_cloth_mesh(
add_springs: bool = False,
spring_ke: float = default_spring_ke,
spring_kd: float = default_spring_kd,
particle_radius: float = default_particle_radius,
):
"""Helper to create a cloth model from a regular triangle mesh
@@ -3989,6 +3991,7 @@ def add_cloth_mesh(
density: The density per-area of the mesh
edge_callback: A user callback when an edge is created
face_callback: A user callback when a face is created
particle_radius: The particle_radius which controls particle based collisions.
Note:
The mesh should be two manifold.
@@ -4002,7 +4005,7 @@ def add_cloth_mesh(
for v in vertices:
p = wp.quat_rotate(rot, v * scale) + pos

self.add_particle(p, vel, 0.0)
self.add_particle(p, vel, 0.0, radius=particle_radius)

# triangles
inds = start_vertex + np.array(indices)
110 changes: 110 additions & 0 deletions warp/tests/test_collision.py
Original file line number Diff line number Diff line change
@@ -7,8 +7,11 @@

import unittest

import warp as wp
import warp.examples
import warp.sim
from warp.sim.collide import *
from warp.sim.integrator_euler import eval_triangles_contact
from warp.tests.unittest_utils import *


@@ -474,12 +477,119 @@ def get_data():
devices = get_test_devices()


def test_particle_collision(test, device):
with wp.ScopedDevice(device):
contact_radius = 1.23
builder1 = wp.sim.ModelBuilder()
builder1.add_cloth_grid(
pos=wp.vec3(0.0, 0.0, 0.0),
rot=wp.quat_identity(),
vel=wp.vec3(0.0, 0.0, 0.0),
dim_x=100,
dim_y=100,
cell_x=0.1,
cell_y=0.1,
mass=0.1,
particle_radius=contact_radius,
)

cloth_grid = builder1.finalize()
cloth_grid_particle_radius = cloth_grid.particle_radius.numpy()
assert_np_equal(cloth_grid_particle_radius, np.full(cloth_grid_particle_radius.shape, contact_radius), tol=1e-5)

vertices = [
[2.0, 0.0, 0.0],
[2.0, 2.0, 0.0],
[0.0, 0.0, 0.0],
[1.0, 0.0, 1.0],
[1.0, 1.0, 1.0],
[0.0, 0.0, 1.0],
]
vertices = [wp.vec3(v) for v in vertices]
faces = [0, 1, 2, 3, 4, 5]

builder2 = wp.sim.ModelBuilder()
builder2.add_cloth_mesh(
pos=wp.vec3(0.0, 0.0, 0.0),
rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), 0.0),
scale=1.0,
vertices=vertices,
indices=faces,
tri_ke=1e4,
tri_ka=1e4,
tri_kd=1e-5,
edge_ke=10,
edge_kd=0.0,
vel=wp.vec3(0.0, 0.0, 0.0),
density=0.1,
particle_radius=contact_radius,
)
cloth_mesh = builder2.finalize()
cloth_mesh_particle_radius = cloth_mesh.particle_radius.numpy()
assert_np_equal(cloth_mesh_particle_radius, np.full(cloth_mesh_particle_radius.shape, contact_radius), tol=1e-5)

state = cloth_mesh.state()
particle_f = wp.zeros_like(state.particle_q)
wp.launch(
kernel=eval_triangles_contact,
dim=cloth_mesh.tri_count * cloth_mesh.particle_count,
inputs=[
cloth_mesh.particle_count,
state.particle_q,
state.particle_qd,
cloth_mesh.tri_indices,
cloth_mesh.tri_materials,
cloth_mesh.particle_radius,
],
outputs=[particle_f],
)
test.assertTrue((np.linalg.norm(particle_f.numpy(), axis=1) != 0).all())

builder3 = wp.sim.ModelBuilder()
builder3.add_cloth_mesh(
pos=wp.vec3(0.0, 0.0, 0.0),
rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), 0.0),
scale=1.0,
vertices=vertices,
indices=faces,
tri_ke=1e4,
tri_ka=1e4,
tri_kd=1e-5,
edge_ke=10,
edge_kd=0.0,
vel=wp.vec3(0.0, 0.0, 0.0),
density=0.1,
particle_radius=0.5,
)
cloth_mesh_2 = builder3.finalize()
cloth_mesh_2_particle_radius = cloth_mesh_2.particle_radius.numpy()
assert_np_equal(cloth_mesh_2_particle_radius, np.full(cloth_mesh_2_particle_radius.shape, 0.5), tol=1e-5)

state_2 = cloth_mesh_2.state()
particle_f_2 = wp.zeros_like(cloth_mesh_2.particle_q)
wp.launch(
kernel=eval_triangles_contact,
dim=cloth_mesh_2.tri_count * cloth_mesh_2.particle_count,
inputs=[
cloth_mesh_2.particle_count,
state_2.particle_q,
state_2.particle_qd,
cloth_mesh_2.tri_indices,
cloth_mesh_2.tri_materials,
cloth_mesh_2.particle_radius,
],
outputs=[particle_f_2],
)
test.assertTrue((np.linalg.norm(particle_f_2.numpy(), axis=1) == 0).all())


class TestCollision(unittest.TestCase):
pass


add_function_test(TestCollision, "test_vertex_triangle_collision", test_vertex_triangle_collision, devices=devices)
add_function_test(TestCollision, "test_edge_edge_collision", test_vertex_triangle_collision, devices=devices)
add_function_test(TestCollision, "test_particle_collision", test_particle_collision, devices=devices)

if __name__ == "__main__":
wp.clear_kernel_cache()

0 comments on commit eacad4f

Please sign in to comment.