warp-lang 1.7.0__py3-none-macosx_10_13_universal2.whl → 1.7.1__py3-none-macosx_10_13_universal2.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of warp-lang might be problematic. Click here for more details.
- warp/autograd.py +12 -2
- warp/build.py +1 -1
- warp/builtins.py +11 -10
- warp/codegen.py +17 -5
- warp/config.py +1 -1
- warp/context.py +6 -0
- warp/examples/benchmarks/benchmark_cloth.py +1 -1
- warp/examples/distributed/example_jacobi_mpi.py +507 -0
- warp/fem/field/field.py +11 -1
- warp/fem/field/nodal_field.py +36 -22
- warp/fem/geometry/adaptive_nanogrid.py +7 -3
- warp/fem/geometry/trimesh.py +4 -12
- warp/jax_experimental/custom_call.py +14 -2
- warp/jax_experimental/ffi.py +5 -1
- warp/native/tile.h +11 -11
- warp/native/warp.cu +1 -1
- warp/render/render_opengl.py +19 -17
- warp/render/render_usd.py +93 -3
- warp/sim/collide.py +11 -9
- warp/sim/inertia.py +189 -156
- warp/sim/integrator_euler.py +3 -0
- warp/sim/integrator_xpbd.py +3 -0
- warp/sim/model.py +29 -12
- warp/sim/render.py +4 -0
- warp/stubs.py +1 -1
- warp/tests/assets/torus.usda +1 -1
- warp/tests/sim/test_collision.py +237 -206
- warp/tests/sim/test_inertia.py +161 -0
- warp/tests/sim/{flaky_test_sim_grad.py → test_sim_grad.py} +4 -0
- warp/tests/sim/test_xpbd.py +399 -0
- warp/tests/test_codegen.py +24 -3
- warp/tests/test_examples.py +16 -6
- warp/tests/test_fem.py +75 -10
- warp/tests/test_mat.py +370 -103
- warp/tests/test_quat.py +321 -137
- warp/tests/test_vec.py +320 -174
- warp/tests/tile/test_tile_load.py +97 -0
- warp/tests/unittest_suites.py +2 -5
- warp/types.py +65 -8
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.1.dist-info}/METADATA +21 -9
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.1.dist-info}/RECORD +44 -41
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.1.dist-info}/WHEEL +1 -1
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.1.dist-info}/licenses/LICENSE.md +0 -26
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.1.dist-info}/top_level.txt +0 -0
warp/tests/sim/test_collision.py
CHANGED
|
@@ -18,7 +18,12 @@ import unittest
|
|
|
18
18
|
import warp as wp
|
|
19
19
|
import warp.examples
|
|
20
20
|
import warp.sim
|
|
21
|
-
from warp.sim.collide import
|
|
21
|
+
from warp.sim.collide import (
|
|
22
|
+
TriMeshCollisionDetector,
|
|
23
|
+
init_triangle_collision_data_kernel,
|
|
24
|
+
triangle_closest_point,
|
|
25
|
+
vertex_adjacent_to_triangle,
|
|
26
|
+
)
|
|
22
27
|
from warp.sim.integrator_euler import eval_triangles_contact
|
|
23
28
|
from warp.tests.unittest_utils import *
|
|
24
29
|
|
|
@@ -260,229 +265,205 @@ def validate_edge_collisions(
|
|
|
260
265
|
wp.expect_eq(dist >= min_dist, True)
|
|
261
266
|
|
|
262
267
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
self.device = device
|
|
268
|
+
def init_model(vs, fs, device):
|
|
269
|
+
vertices = [wp.vec3(v) for v in vs]
|
|
266
270
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
271
|
+
builder = wp.sim.ModelBuilder()
|
|
272
|
+
builder.add_cloth_mesh(
|
|
273
|
+
pos=wp.vec3(0.0, 200.0, 0.0),
|
|
274
|
+
rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), 0.0),
|
|
275
|
+
scale=1.0,
|
|
276
|
+
vertices=vertices,
|
|
277
|
+
indices=fs,
|
|
278
|
+
vel=wp.vec3(0.0, 0.0, 0.0),
|
|
279
|
+
density=0.02,
|
|
280
|
+
tri_ke=0,
|
|
281
|
+
tri_ka=0,
|
|
282
|
+
tri_kd=0,
|
|
283
|
+
)
|
|
284
|
+
model = builder.finalize(device=device)
|
|
270
285
|
|
|
271
|
-
|
|
272
|
-
builder.add_cloth_mesh(
|
|
273
|
-
pos=wp.vec3(0.0, 200.0, 0.0),
|
|
274
|
-
rot=wp.quat_from_axis_angle(wp.vec3(1.0, 0.0, 0.0), 0.0),
|
|
275
|
-
scale=1.0,
|
|
276
|
-
vertices=vertices,
|
|
277
|
-
indices=fs,
|
|
278
|
-
vel=wp.vec3(0.0, 0.0, 0.0),
|
|
279
|
-
density=0.02,
|
|
280
|
-
tri_ke=0,
|
|
281
|
-
tri_ka=0,
|
|
282
|
-
tri_kd=0,
|
|
283
|
-
)
|
|
284
|
-
self.model = builder.finalize(device=self.device)
|
|
285
|
-
|
|
286
|
-
self.collision_detector = TriMeshCollisionDetector(model=self.model)
|
|
287
|
-
|
|
288
|
-
def run_vertex_triangle_test(self):
|
|
289
|
-
rs = [1e-2, 2e-2, 5e-2, 1e-1]
|
|
290
|
-
for query_radius in rs:
|
|
291
|
-
self.collision_detector.vertex_triangle_collision_detection(query_radius)
|
|
292
|
-
vertex_colliding_triangles_count_1 = self.collision_detector.vertex_colliding_triangles_count.numpy()
|
|
293
|
-
vertex_min_dis_1 = self.collision_detector.vertex_colliding_triangles_min_dist.numpy()
|
|
294
|
-
|
|
295
|
-
triangle_colliding_vertices_count_1 = self.collision_detector.triangle_colliding_vertices_count.numpy()
|
|
296
|
-
triangle_min_dis_1 = self.collision_detector.triangle_colliding_vertices_min_dist.numpy()
|
|
297
|
-
|
|
298
|
-
wp.launch(
|
|
299
|
-
kernel=init_triangle_collision_data_kernel,
|
|
300
|
-
inputs=[
|
|
301
|
-
query_radius,
|
|
302
|
-
self.collision_detector.triangle_colliding_vertices_count,
|
|
303
|
-
self.collision_detector.triangle_colliding_vertices_min_dist,
|
|
304
|
-
self.collision_detector.resize_flags,
|
|
305
|
-
],
|
|
306
|
-
dim=self.model.tri_count,
|
|
307
|
-
device=self.model.device,
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
wp.launch(
|
|
311
|
-
kernel=vertex_triangle_collision_detection_brute_force,
|
|
312
|
-
inputs=[
|
|
313
|
-
query_radius,
|
|
314
|
-
self.collision_detector.bvh_tris.id,
|
|
315
|
-
self.collision_detector.model.particle_q,
|
|
316
|
-
self.collision_detector.model.tri_indices,
|
|
317
|
-
self.collision_detector.vertex_colliding_triangles,
|
|
318
|
-
self.collision_detector.vertex_colliding_triangles_count,
|
|
319
|
-
self.collision_detector.vertex_colliding_triangles_offsets,
|
|
320
|
-
self.collision_detector.vertex_colliding_triangles_buffer_sizes,
|
|
321
|
-
self.collision_detector.vertex_colliding_triangles_min_dist,
|
|
322
|
-
self.collision_detector.triangle_colliding_vertices,
|
|
323
|
-
self.collision_detector.triangle_colliding_vertices_count,
|
|
324
|
-
self.collision_detector.triangle_colliding_vertices_offsets,
|
|
325
|
-
self.collision_detector.triangle_colliding_vertices_buffer_sizes,
|
|
326
|
-
self.collision_detector.triangle_colliding_vertices_min_dist,
|
|
327
|
-
self.collision_detector.resize_flags,
|
|
328
|
-
],
|
|
329
|
-
dim=self.model.particle_count,
|
|
330
|
-
device=self.model.device,
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
vertex_colliding_triangles_count_2 = self.collision_detector.vertex_colliding_triangles_count.numpy()
|
|
334
|
-
vertex_min_dis_2 = self.collision_detector.vertex_colliding_triangles_min_dist.numpy()
|
|
335
|
-
|
|
336
|
-
triangle_colliding_vertices_count_2 = self.collision_detector.triangle_colliding_vertices_count.numpy()
|
|
337
|
-
triangle_min_dis_2 = self.collision_detector.triangle_colliding_vertices_min_dist.numpy()
|
|
338
|
-
|
|
339
|
-
assert (vertex_colliding_triangles_count_2 == vertex_colliding_triangles_count_1).all()
|
|
340
|
-
assert (triangle_min_dis_2 == triangle_min_dis_1).all()
|
|
341
|
-
assert (triangle_colliding_vertices_count_2 == triangle_colliding_vertices_count_1).all()
|
|
342
|
-
assert (vertex_min_dis_2 == vertex_min_dis_1).all()
|
|
343
|
-
|
|
344
|
-
wp.launch(
|
|
345
|
-
kernel=validate_vertex_collisions,
|
|
346
|
-
inputs=[
|
|
347
|
-
query_radius,
|
|
348
|
-
self.collision_detector.bvh_tris.id,
|
|
349
|
-
self.collision_detector.model.particle_q,
|
|
350
|
-
self.collision_detector.model.tri_indices,
|
|
351
|
-
self.collision_detector.vertex_colliding_triangles,
|
|
352
|
-
self.collision_detector.vertex_colliding_triangles_count,
|
|
353
|
-
self.collision_detector.vertex_colliding_triangles_offsets,
|
|
354
|
-
self.collision_detector.vertex_colliding_triangles_buffer_sizes,
|
|
355
|
-
self.collision_detector.vertex_colliding_triangles_min_dist,
|
|
356
|
-
self.collision_detector.resize_flags,
|
|
357
|
-
],
|
|
358
|
-
dim=self.model.particle_count,
|
|
359
|
-
device=self.model.device,
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
wp.launch(
|
|
363
|
-
kernel=validate_triangle_collisions,
|
|
364
|
-
inputs=[
|
|
365
|
-
query_radius,
|
|
366
|
-
self.collision_detector.bvh_tris.id,
|
|
367
|
-
self.collision_detector.model.particle_q,
|
|
368
|
-
self.collision_detector.model.tri_indices,
|
|
369
|
-
self.collision_detector.triangle_colliding_vertices,
|
|
370
|
-
self.collision_detector.triangle_colliding_vertices_count,
|
|
371
|
-
self.collision_detector.triangle_colliding_vertices_offsets,
|
|
372
|
-
self.collision_detector.triangle_colliding_vertices_buffer_sizes,
|
|
373
|
-
self.collision_detector.triangle_colliding_vertices_min_dist,
|
|
374
|
-
self.collision_detector.resize_flags,
|
|
375
|
-
],
|
|
376
|
-
dim=self.model.tri_count,
|
|
377
|
-
device=self.model.device,
|
|
378
|
-
)
|
|
379
|
-
|
|
380
|
-
def run_edge_edge_test(self):
|
|
381
|
-
rs = [1e-2, 2e-2, 5e-2, 1e-1]
|
|
382
|
-
edge_edge_parallel_epsilon = 1e-5
|
|
383
|
-
for query_radius in rs:
|
|
384
|
-
self.collision_detector.edge_edge_collision_detection(query_radius)
|
|
385
|
-
edge_colliding_edges_count_1 = self.collision_detector.edge_colliding_edges_count.numpy()
|
|
386
|
-
edge_min_dist_1 = self.collision_detector.edge_colliding_edges_min_dist.numpy()
|
|
387
|
-
|
|
388
|
-
print(edge_colliding_edges_count_1)
|
|
389
|
-
|
|
390
|
-
wp.launch(
|
|
391
|
-
kernel=edge_edge_collision_detection_brute_force,
|
|
392
|
-
inputs=[
|
|
393
|
-
query_radius,
|
|
394
|
-
self.collision_detector.bvh_edges.id,
|
|
395
|
-
self.collision_detector.model.particle_q,
|
|
396
|
-
self.collision_detector.model.edge_indices,
|
|
397
|
-
self.collision_detector.edge_colliding_edges_offsets,
|
|
398
|
-
self.collision_detector.edge_colliding_edges_buffer_sizes,
|
|
399
|
-
edge_edge_parallel_epsilon,
|
|
400
|
-
],
|
|
401
|
-
outputs=[
|
|
402
|
-
self.collision_detector.edge_colliding_edges,
|
|
403
|
-
self.collision_detector.edge_colliding_edges_count,
|
|
404
|
-
self.collision_detector.edge_colliding_edges_min_dist,
|
|
405
|
-
self.collision_detector.resize_flags,
|
|
406
|
-
],
|
|
407
|
-
dim=self.model.edge_count,
|
|
408
|
-
device=self.model.device,
|
|
409
|
-
)
|
|
410
|
-
|
|
411
|
-
edge_colliding_edges_count_2 = self.collision_detector.edge_colliding_edges_count.numpy()
|
|
412
|
-
edge_min_dist_2 = self.collision_detector.edge_colliding_edges_min_dist.numpy()
|
|
413
|
-
|
|
414
|
-
assert (edge_colliding_edges_count_2 == edge_colliding_edges_count_1).all()
|
|
415
|
-
assert (edge_min_dist_1 == edge_min_dist_2).all()
|
|
416
|
-
|
|
417
|
-
wp.launch(
|
|
418
|
-
kernel=validate_edge_collisions,
|
|
419
|
-
inputs=[
|
|
420
|
-
query_radius,
|
|
421
|
-
self.collision_detector.bvh_edges.id,
|
|
422
|
-
self.collision_detector.model.particle_q,
|
|
423
|
-
self.collision_detector.model.edge_indices,
|
|
424
|
-
self.collision_detector.edge_colliding_edges_offsets,
|
|
425
|
-
self.collision_detector.edge_colliding_edges_buffer_sizes,
|
|
426
|
-
edge_edge_parallel_epsilon,
|
|
427
|
-
],
|
|
428
|
-
outputs=[
|
|
429
|
-
self.collision_detector.edge_colliding_edges,
|
|
430
|
-
self.collision_detector.edge_colliding_edges_count,
|
|
431
|
-
self.collision_detector.edge_colliding_edges_min_dist,
|
|
432
|
-
self.collision_detector.resize_flags,
|
|
433
|
-
],
|
|
434
|
-
dim=self.model.particle_count,
|
|
435
|
-
device=self.model.device,
|
|
436
|
-
)
|
|
437
|
-
|
|
438
|
-
def set_points_fixed(self, model, fixed_particles):
|
|
439
|
-
if len(fixed_particles):
|
|
440
|
-
flags = model.particle_flags.numpy()
|
|
441
|
-
for fixed_vertex_id in fixed_particles:
|
|
442
|
-
flags[fixed_vertex_id] = wp.uint32(int(flags[fixed_vertex_id]) & ~int(PARTICLE_FLAG_ACTIVE))
|
|
443
|
-
|
|
444
|
-
model.particle_flags = wp.array(flags, device=model.device)
|
|
286
|
+
collision_detector = TriMeshCollisionDetector(model=model)
|
|
445
287
|
|
|
288
|
+
return model, collision_detector
|
|
446
289
|
|
|
447
|
-
|
|
448
|
-
def
|
|
290
|
+
|
|
291
|
+
def get_data():
|
|
449
292
|
from pxr import Usd, UsdGeom
|
|
450
293
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
294
|
+
usd_stage = Usd.Stage.Open(os.path.join(warp.examples.get_asset_directory(), "bunny.usd"))
|
|
295
|
+
usd_geom = UsdGeom.Mesh(usd_stage.GetPrimAtPath("/root/bunny"))
|
|
296
|
+
|
|
297
|
+
vertices = np.array(usd_geom.GetPointsAttr().Get())
|
|
298
|
+
faces = np.array(usd_geom.GetFaceVertexIndicesAttr().Get())
|
|
454
299
|
|
|
455
|
-
|
|
456
|
-
faces = np.array(usd_geom.GetFaceVertexIndicesAttr().Get())
|
|
300
|
+
return vertices, faces
|
|
457
301
|
|
|
458
|
-
return vertices, faces
|
|
459
302
|
|
|
303
|
+
@unittest.skipUnless(USD_AVAILABLE, "Requires usd-core")
|
|
304
|
+
def test_vertex_triangle_collision(test, device):
|
|
460
305
|
vertices, faces = get_data()
|
|
461
306
|
|
|
462
|
-
|
|
463
|
-
sim.run_vertex_triangle_test()
|
|
307
|
+
model, collision_detector = init_model(vertices, faces, device)
|
|
464
308
|
|
|
309
|
+
rs = [1e-2, 2e-2, 5e-2, 1e-1]
|
|
465
310
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
311
|
+
for query_radius in rs:
|
|
312
|
+
collision_detector.vertex_triangle_collision_detection(query_radius)
|
|
313
|
+
vertex_colliding_triangles_count_1 = collision_detector.vertex_colliding_triangles_count.numpy()
|
|
314
|
+
vertex_min_dis_1 = collision_detector.vertex_colliding_triangles_min_dist.numpy()
|
|
469
315
|
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
usd_geom = UsdGeom.Mesh(usd_stage.GetPrimAtPath("/root/bunny"))
|
|
316
|
+
triangle_colliding_vertices_count_1 = collision_detector.triangle_colliding_vertices_count.numpy()
|
|
317
|
+
triangle_min_dis_1 = collision_detector.triangle_colliding_vertices_min_dist.numpy()
|
|
473
318
|
|
|
474
|
-
|
|
475
|
-
|
|
319
|
+
wp.launch(
|
|
320
|
+
kernel=init_triangle_collision_data_kernel,
|
|
321
|
+
inputs=[
|
|
322
|
+
query_radius,
|
|
323
|
+
collision_detector.triangle_colliding_vertices_count,
|
|
324
|
+
collision_detector.triangle_colliding_vertices_min_dist,
|
|
325
|
+
collision_detector.resize_flags,
|
|
326
|
+
],
|
|
327
|
+
dim=model.tri_count,
|
|
328
|
+
device=device,
|
|
329
|
+
)
|
|
476
330
|
|
|
477
|
-
|
|
331
|
+
wp.launch(
|
|
332
|
+
kernel=vertex_triangle_collision_detection_brute_force,
|
|
333
|
+
inputs=[
|
|
334
|
+
query_radius,
|
|
335
|
+
collision_detector.bvh_tris.id,
|
|
336
|
+
collision_detector.model.particle_q,
|
|
337
|
+
collision_detector.model.tri_indices,
|
|
338
|
+
collision_detector.vertex_colliding_triangles,
|
|
339
|
+
collision_detector.vertex_colliding_triangles_count,
|
|
340
|
+
collision_detector.vertex_colliding_triangles_offsets,
|
|
341
|
+
collision_detector.vertex_colliding_triangles_buffer_sizes,
|
|
342
|
+
collision_detector.vertex_colliding_triangles_min_dist,
|
|
343
|
+
collision_detector.triangle_colliding_vertices,
|
|
344
|
+
collision_detector.triangle_colliding_vertices_count,
|
|
345
|
+
collision_detector.triangle_colliding_vertices_offsets,
|
|
346
|
+
collision_detector.triangle_colliding_vertices_buffer_sizes,
|
|
347
|
+
collision_detector.triangle_colliding_vertices_min_dist,
|
|
348
|
+
collision_detector.resize_flags,
|
|
349
|
+
],
|
|
350
|
+
dim=model.particle_count,
|
|
351
|
+
device=device,
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
vertex_colliding_triangles_count_2 = collision_detector.vertex_colliding_triangles_count.numpy()
|
|
355
|
+
vertex_min_dis_2 = collision_detector.vertex_colliding_triangles_min_dist.numpy()
|
|
356
|
+
|
|
357
|
+
triangle_colliding_vertices_count_2 = collision_detector.triangle_colliding_vertices_count.numpy()
|
|
358
|
+
triangle_min_dis_2 = collision_detector.triangle_colliding_vertices_min_dist.numpy()
|
|
359
|
+
|
|
360
|
+
assert_np_equal(vertex_colliding_triangles_count_2, vertex_colliding_triangles_count_1)
|
|
361
|
+
assert_np_equal(triangle_min_dis_2, triangle_min_dis_1)
|
|
362
|
+
assert_np_equal(triangle_colliding_vertices_count_2, triangle_colliding_vertices_count_1)
|
|
363
|
+
assert_np_equal(vertex_min_dis_2, vertex_min_dis_1)
|
|
364
|
+
|
|
365
|
+
wp.launch(
|
|
366
|
+
kernel=validate_vertex_collisions,
|
|
367
|
+
inputs=[
|
|
368
|
+
query_radius,
|
|
369
|
+
collision_detector.bvh_tris.id,
|
|
370
|
+
collision_detector.model.particle_q,
|
|
371
|
+
collision_detector.model.tri_indices,
|
|
372
|
+
collision_detector.vertex_colliding_triangles,
|
|
373
|
+
collision_detector.vertex_colliding_triangles_count,
|
|
374
|
+
collision_detector.vertex_colliding_triangles_offsets,
|
|
375
|
+
collision_detector.vertex_colliding_triangles_buffer_sizes,
|
|
376
|
+
collision_detector.vertex_colliding_triangles_min_dist,
|
|
377
|
+
collision_detector.resize_flags,
|
|
378
|
+
],
|
|
379
|
+
dim=model.particle_count,
|
|
380
|
+
device=device,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
wp.launch(
|
|
384
|
+
kernel=validate_triangle_collisions,
|
|
385
|
+
inputs=[
|
|
386
|
+
query_radius,
|
|
387
|
+
collision_detector.bvh_tris.id,
|
|
388
|
+
collision_detector.model.particle_q,
|
|
389
|
+
collision_detector.model.tri_indices,
|
|
390
|
+
collision_detector.triangle_colliding_vertices,
|
|
391
|
+
collision_detector.triangle_colliding_vertices_count,
|
|
392
|
+
collision_detector.triangle_colliding_vertices_offsets,
|
|
393
|
+
collision_detector.triangle_colliding_vertices_buffer_sizes,
|
|
394
|
+
collision_detector.triangle_colliding_vertices_min_dist,
|
|
395
|
+
collision_detector.resize_flags,
|
|
396
|
+
],
|
|
397
|
+
dim=model.tri_count,
|
|
398
|
+
device=device,
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
wp.synchronize_device(device)
|
|
478
402
|
|
|
403
|
+
|
|
404
|
+
@unittest.skipUnless(USD_AVAILABLE, "Requires usd-core")
|
|
405
|
+
def test_edge_edge_collision(test, device):
|
|
479
406
|
vertices, faces = get_data()
|
|
480
407
|
|
|
481
|
-
|
|
482
|
-
|
|
408
|
+
model, collision_detector = init_model(vertices, faces, device)
|
|
409
|
+
|
|
410
|
+
rs = [1e-2, 2e-2, 5e-2, 1e-1]
|
|
411
|
+
edge_edge_parallel_epsilon = 1e-5
|
|
412
|
+
|
|
413
|
+
for query_radius in rs:
|
|
414
|
+
collision_detector.edge_edge_collision_detection(query_radius)
|
|
415
|
+
edge_colliding_edges_count_1 = collision_detector.edge_colliding_edges_count.numpy()
|
|
416
|
+
edge_min_dist_1 = collision_detector.edge_colliding_edges_min_dist.numpy()
|
|
417
|
+
|
|
418
|
+
wp.launch(
|
|
419
|
+
kernel=edge_edge_collision_detection_brute_force,
|
|
420
|
+
inputs=[
|
|
421
|
+
query_radius,
|
|
422
|
+
collision_detector.bvh_edges.id,
|
|
423
|
+
collision_detector.model.particle_q,
|
|
424
|
+
collision_detector.model.edge_indices,
|
|
425
|
+
collision_detector.edge_colliding_edges_offsets,
|
|
426
|
+
collision_detector.edge_colliding_edges_buffer_sizes,
|
|
427
|
+
edge_edge_parallel_epsilon,
|
|
428
|
+
],
|
|
429
|
+
outputs=[
|
|
430
|
+
collision_detector.edge_colliding_edges,
|
|
431
|
+
collision_detector.edge_colliding_edges_count,
|
|
432
|
+
collision_detector.edge_colliding_edges_min_dist,
|
|
433
|
+
collision_detector.resize_flags,
|
|
434
|
+
],
|
|
435
|
+
dim=model.edge_count,
|
|
436
|
+
device=device,
|
|
437
|
+
)
|
|
483
438
|
|
|
439
|
+
edge_colliding_edges_count_2 = collision_detector.edge_colliding_edges_count.numpy()
|
|
440
|
+
edge_min_dist_2 = collision_detector.edge_colliding_edges_min_dist.numpy()
|
|
441
|
+
|
|
442
|
+
assert_np_equal(edge_colliding_edges_count_2, edge_colliding_edges_count_1)
|
|
443
|
+
assert_np_equal(edge_min_dist_2, edge_min_dist_1)
|
|
444
|
+
|
|
445
|
+
wp.launch(
|
|
446
|
+
kernel=validate_edge_collisions,
|
|
447
|
+
inputs=[
|
|
448
|
+
query_radius,
|
|
449
|
+
collision_detector.bvh_edges.id,
|
|
450
|
+
collision_detector.model.particle_q,
|
|
451
|
+
collision_detector.model.edge_indices,
|
|
452
|
+
collision_detector.edge_colliding_edges_offsets,
|
|
453
|
+
collision_detector.edge_colliding_edges_buffer_sizes,
|
|
454
|
+
edge_edge_parallel_epsilon,
|
|
455
|
+
],
|
|
456
|
+
outputs=[
|
|
457
|
+
collision_detector.edge_colliding_edges,
|
|
458
|
+
collision_detector.edge_colliding_edges_count,
|
|
459
|
+
collision_detector.edge_colliding_edges_min_dist,
|
|
460
|
+
collision_detector.resize_flags,
|
|
461
|
+
],
|
|
462
|
+
dim=model.particle_count,
|
|
463
|
+
device=device,
|
|
464
|
+
)
|
|
484
465
|
|
|
485
|
-
|
|
466
|
+
wp.synchronize_device(device)
|
|
486
467
|
|
|
487
468
|
|
|
488
469
|
def test_particle_collision(test, device):
|
|
@@ -591,14 +572,64 @@ def test_particle_collision(test, device):
|
|
|
591
572
|
test.assertTrue((np.linalg.norm(particle_f_2.numpy(), axis=1) == 0).all())
|
|
592
573
|
|
|
593
574
|
|
|
575
|
+
def test_mesh_ground_collision_index(test, device):
|
|
576
|
+
# create a mesh with 1 triangle for testing
|
|
577
|
+
vertices = np.array(
|
|
578
|
+
[
|
|
579
|
+
[0.0, 0.0, 0.0],
|
|
580
|
+
[1.0, 1.0, 0.0],
|
|
581
|
+
[0.5, 2.0, 0.0],
|
|
582
|
+
]
|
|
583
|
+
)
|
|
584
|
+
mesh = wp.sim.Mesh(vertices=vertices, indices=[0, 1, 2])
|
|
585
|
+
builder = wp.sim.ModelBuilder()
|
|
586
|
+
# create body with nonzero mass to ensure it is not static
|
|
587
|
+
# and contact points will be computed
|
|
588
|
+
b = builder.add_body(m=1.0)
|
|
589
|
+
builder.add_shape_mesh(
|
|
590
|
+
body=b,
|
|
591
|
+
mesh=mesh,
|
|
592
|
+
has_shape_collision=False,
|
|
593
|
+
)
|
|
594
|
+
# add another mesh that is not in contact
|
|
595
|
+
b2 = builder.add_body(m=1.0, origin=wp.transform((0.0, 3.0, 0.0), wp.quat_identity()))
|
|
596
|
+
builder.add_shape_mesh(
|
|
597
|
+
body=b2,
|
|
598
|
+
mesh=mesh,
|
|
599
|
+
has_shape_collision=False,
|
|
600
|
+
)
|
|
601
|
+
model = builder.finalize(device=device)
|
|
602
|
+
test.assertEqual(model.rigid_contact_max, 6)
|
|
603
|
+
test.assertEqual(model.shape_contact_pair_count, 0)
|
|
604
|
+
test.assertEqual(model.shape_ground_contact_pair_count, 2)
|
|
605
|
+
model.ground = True
|
|
606
|
+
# ensure all the mesh vertices will be within the contact margin
|
|
607
|
+
model.rigid_contact_margin = 2.0
|
|
608
|
+
state = model.state()
|
|
609
|
+
wp.sim.collide(model, state)
|
|
610
|
+
test.assertEqual(model.rigid_contact_count.numpy()[0], 3)
|
|
611
|
+
tids = model.rigid_contact_tids.list()
|
|
612
|
+
test.assertEqual(sorted(tids), [-1, -1, -1, 0, 1, 2])
|
|
613
|
+
tids = [t for t in tids if t != -1]
|
|
614
|
+
# retrieve the mesh vertices from the contact thread indices
|
|
615
|
+
assert_np_equal(model.rigid_contact_point0.numpy()[:3], vertices[tids])
|
|
616
|
+
assert_np_equal(model.rigid_contact_point1.numpy()[:3, 0], vertices[tids, 0])
|
|
617
|
+
assert_np_equal(model.rigid_contact_point1.numpy()[:3, 1:], np.zeros((3, 2)))
|
|
618
|
+
assert_np_equal(model.rigid_contact_normal.numpy()[:3], np.tile([0.0, 1.0, 0.0], (3, 1)))
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
devices = get_test_devices(mode="basic")
|
|
622
|
+
|
|
623
|
+
|
|
594
624
|
class TestCollision(unittest.TestCase):
|
|
595
625
|
pass
|
|
596
626
|
|
|
597
627
|
|
|
598
|
-
|
|
599
|
-
|
|
628
|
+
add_function_test(TestCollision, "test_vertex_triangle_collision", test_vertex_triangle_collision, devices=devices)
|
|
629
|
+
add_function_test(TestCollision, "test_edge_edge_collision", test_edge_edge_collision, devices=devices)
|
|
600
630
|
add_function_test(TestCollision, "test_particle_collision", test_particle_collision, devices=devices)
|
|
631
|
+
add_function_test(TestCollision, "test_mesh_ground_collision_index", test_mesh_ground_collision_index, devices=devices)
|
|
601
632
|
|
|
602
633
|
if __name__ == "__main__":
|
|
603
634
|
wp.clear_kernel_cache()
|
|
604
|
-
unittest.main(verbosity=2)
|
|
635
|
+
unittest.main(verbosity=2, failfast=True)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
|
|
16
|
+
import unittest
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
|
|
20
|
+
import warp as wp
|
|
21
|
+
from warp.render.render_opengl import OpenGLRenderer
|
|
22
|
+
from warp.sim import ModelBuilder
|
|
23
|
+
from warp.sim.inertia import (
|
|
24
|
+
compute_box_inertia,
|
|
25
|
+
compute_mesh_inertia,
|
|
26
|
+
compute_sphere_inertia,
|
|
27
|
+
)
|
|
28
|
+
from warp.tests.unittest_utils import assert_np_equal
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestInertia(unittest.TestCase):
|
|
32
|
+
def test_cube_mesh_inertia(self):
|
|
33
|
+
# Unit cube
|
|
34
|
+
vertices = [
|
|
35
|
+
[1.0, 0.0, 0.0],
|
|
36
|
+
[1.0, 0.0, 1.0],
|
|
37
|
+
[0.0, 0.0, 1.0],
|
|
38
|
+
[0.0, 0.0, 0.0],
|
|
39
|
+
[1.0, 1.0, 0.0],
|
|
40
|
+
[1.0, 1.0, 1.0],
|
|
41
|
+
[0.0, 1.0, 1.0],
|
|
42
|
+
[0.0, 1.0, 0.0],
|
|
43
|
+
]
|
|
44
|
+
indices = [
|
|
45
|
+
[1, 2, 3],
|
|
46
|
+
[7, 6, 5],
|
|
47
|
+
[4, 5, 1],
|
|
48
|
+
[5, 6, 2],
|
|
49
|
+
[2, 6, 7],
|
|
50
|
+
[0, 3, 7],
|
|
51
|
+
[0, 1, 3],
|
|
52
|
+
[4, 7, 5],
|
|
53
|
+
[0, 4, 1],
|
|
54
|
+
[1, 5, 2],
|
|
55
|
+
[3, 2, 7],
|
|
56
|
+
[4, 0, 7],
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
mass_0, com_0, I_0, volume_0 = compute_mesh_inertia(
|
|
60
|
+
density=1000, vertices=vertices, indices=indices, is_solid=True
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
self.assertAlmostEqual(mass_0, 1000.0, delta=1e-6)
|
|
64
|
+
self.assertAlmostEqual(volume_0, 1.0, delta=1e-6)
|
|
65
|
+
assert_np_equal(np.array(com_0), np.array([0.5, 0.5, 0.5]), tol=1e-6)
|
|
66
|
+
|
|
67
|
+
# Check against analytical inertia
|
|
68
|
+
mass_box, com_box, I_box = compute_box_inertia(1000.0, 1.0, 1.0, 1.0)
|
|
69
|
+
self.assertAlmostEqual(mass_box, mass_0, delta=1e-6)
|
|
70
|
+
assert_np_equal(np.array(com_box), np.zeros(3), tol=1e-6)
|
|
71
|
+
assert_np_equal(np.array(I_0), np.array(I_box), tol=1e-4)
|
|
72
|
+
|
|
73
|
+
# Compute hollow box inertia
|
|
74
|
+
mass_0_hollow, com_0_hollow, I_0_hollow, volume_0_hollow = compute_mesh_inertia(
|
|
75
|
+
density=1000,
|
|
76
|
+
vertices=vertices,
|
|
77
|
+
indices=indices,
|
|
78
|
+
is_solid=False,
|
|
79
|
+
thickness=0.1,
|
|
80
|
+
)
|
|
81
|
+
assert_np_equal(np.array(com_0_hollow), np.array([0.5, 0.5, 0.5]), tol=1e-6)
|
|
82
|
+
|
|
83
|
+
# Add vertex between [0.0, 0.0, 0.0] and [1.0, 0.0, 0.0]
|
|
84
|
+
vertices.append([0.5, 0.0, 0.0])
|
|
85
|
+
indices[5] = [0, 8, 7]
|
|
86
|
+
indices.append([8, 3, 7])
|
|
87
|
+
indices[6] = [0, 1, 8]
|
|
88
|
+
indices.append([8, 1, 3])
|
|
89
|
+
|
|
90
|
+
mass_1, com_1, I_1, volume_1 = compute_mesh_inertia(
|
|
91
|
+
density=1000, vertices=vertices, indices=indices, is_solid=True
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Inertia values should be the same as before
|
|
95
|
+
self.assertAlmostEqual(mass_1, mass_0, delta=1e-6)
|
|
96
|
+
self.assertAlmostEqual(volume_1, volume_0, delta=1e-6)
|
|
97
|
+
assert_np_equal(np.array(com_1), np.array([0.5, 0.5, 0.5]), tol=1e-6)
|
|
98
|
+
assert_np_equal(np.array(I_1), np.array(I_0), tol=1e-4)
|
|
99
|
+
|
|
100
|
+
# Compute hollow box inertia
|
|
101
|
+
mass_1_hollow, com_1_hollow, I_1_hollow, volume_1_hollow = compute_mesh_inertia(
|
|
102
|
+
density=1000,
|
|
103
|
+
vertices=vertices,
|
|
104
|
+
indices=indices,
|
|
105
|
+
is_solid=False,
|
|
106
|
+
thickness=0.1,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Inertia values should be the same as before
|
|
110
|
+
self.assertAlmostEqual(mass_1_hollow, mass_0_hollow, delta=2e-3)
|
|
111
|
+
self.assertAlmostEqual(volume_1_hollow, volume_0_hollow, delta=1e-6)
|
|
112
|
+
assert_np_equal(np.array(com_1_hollow), np.array([0.5, 0.5, 0.5]), tol=1e-6)
|
|
113
|
+
assert_np_equal(np.array(I_1_hollow), np.array(I_0_hollow), tol=1e-4)
|
|
114
|
+
|
|
115
|
+
def test_sphere_mesh_inertia(self):
|
|
116
|
+
vertices, indices = OpenGLRenderer._create_sphere_mesh(radius=2.5, num_latitudes=500, num_longitudes=500)
|
|
117
|
+
|
|
118
|
+
offset = np.array([1.0, 2.0, 3.0], dtype=np.float32)
|
|
119
|
+
vertices = vertices[:, :3] + offset
|
|
120
|
+
|
|
121
|
+
mass_mesh, com_mesh, I_mesh, vol_mesh = compute_mesh_inertia(
|
|
122
|
+
density=1000,
|
|
123
|
+
vertices=vertices,
|
|
124
|
+
indices=indices,
|
|
125
|
+
is_solid=True,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Check against analytical inertia
|
|
129
|
+
mass_sphere, _, I_sphere = compute_sphere_inertia(1000.0, 2.5)
|
|
130
|
+
self.assertAlmostEqual(mass_mesh, mass_sphere, delta=1e2)
|
|
131
|
+
assert_np_equal(np.array(com_mesh), np.array(offset), tol=2e-3)
|
|
132
|
+
assert_np_equal(np.array(I_mesh), np.array(I_sphere), tol=4e2)
|
|
133
|
+
# Check volume
|
|
134
|
+
self.assertAlmostEqual(vol_mesh, 4.0 / 3.0 * np.pi * 2.5**3, delta=3e-2)
|
|
135
|
+
|
|
136
|
+
def test_body_inertia(self):
|
|
137
|
+
vertices, indices = OpenGLRenderer._create_sphere_mesh(radius=2.5, num_latitudes=500, num_longitudes=500)
|
|
138
|
+
|
|
139
|
+
offset = np.array([1.0, 2.0, 3.0], dtype=np.float32)
|
|
140
|
+
vertices = vertices[:, :3] + offset
|
|
141
|
+
|
|
142
|
+
builder = ModelBuilder()
|
|
143
|
+
b = builder.add_body()
|
|
144
|
+
tf = wp.transform(wp.vec3(4.0, 5.0, 6.0), wp.quat_rpy(0.5, -0.8, 1.3))
|
|
145
|
+
builder.add_shape_mesh(
|
|
146
|
+
b,
|
|
147
|
+
pos=tf.p,
|
|
148
|
+
rot=tf.q,
|
|
149
|
+
mesh=wp.sim.Mesh(vertices=vertices, indices=indices),
|
|
150
|
+
density=1000.0,
|
|
151
|
+
)
|
|
152
|
+
transformed_com = wp.transform_point(tf, wp.vec3(*offset))
|
|
153
|
+
assert_np_equal(np.array(builder.body_com[0]), np.array(transformed_com), tol=2e-3)
|
|
154
|
+
mass_sphere, _, I_sphere = compute_sphere_inertia(1000.0, 2.5)
|
|
155
|
+
assert_np_equal(np.array(builder.body_inertia[0]), np.array(I_sphere), tol=4e2)
|
|
156
|
+
self.assertAlmostEqual(builder.body_mass[0], mass_sphere, delta=1e2)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
if __name__ == "__main__":
|
|
160
|
+
wp.clear_kernel_cache()
|
|
161
|
+
unittest.main(verbosity=2)
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
# See the License for the specific language governing permissions and
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
|
+
import platform
|
|
16
17
|
import unittest
|
|
17
18
|
|
|
18
19
|
import numpy as np
|
|
@@ -103,6 +104,9 @@ def test_sphere_pushing_on_rails(
|
|
|
103
104
|
static_contacts=True,
|
|
104
105
|
print_grad=False,
|
|
105
106
|
):
|
|
107
|
+
if platform.system() == "Darwin":
|
|
108
|
+
test.skipTest("Crashes on Mac runners")
|
|
109
|
+
|
|
106
110
|
# Two spheres on a rail (prismatic or D6 joint), one is pushed, the other is passive.
|
|
107
111
|
# The absolute distance to a target is measured and gradients are compared for
|
|
108
112
|
# a push that is too far and too close.
|