warp-lang 1.7.0__py3-none-win_amd64.whl → 1.7.2__py3-none-win_amd64.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/bin/warp-clang.dll +0 -0
- warp/bin/warp.dll +0 -0
- warp/build.py +1 -1
- warp/builtins.py +103 -66
- warp/codegen.py +48 -27
- warp/config.py +1 -1
- warp/context.py +112 -49
- warp/examples/benchmarks/benchmark_cloth.py +1 -1
- warp/examples/distributed/example_jacobi_mpi.py +507 -0
- warp/fem/cache.py +1 -1
- 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 +100 -67
- warp/native/builtin.h +91 -65
- warp/native/svd.h +59 -49
- warp/native/tile.h +55 -26
- warp/native/volume.cpp +2 -2
- warp/native/volume_builder.cu +33 -22
- warp/native/warp.cu +1 -1
- warp/render/render_opengl.py +41 -34
- warp/render/render_usd.py +96 -6
- 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 +56 -31
- warp/sim/render.py +4 -0
- warp/sparse.py +1 -1
- warp/stubs.py +73 -25
- warp/tests/assets/torus.usda +1 -1
- warp/tests/cuda/test_streams.py +1 -1
- warp/tests/sim/test_collision.py +237 -206
- warp/tests/sim/test_inertia.py +161 -0
- warp/tests/sim/test_model.py +5 -3
- warp/tests/sim/{flaky_test_sim_grad.py → test_sim_grad.py} +1 -4
- warp/tests/sim/test_xpbd.py +399 -0
- warp/tests/test_array.py +8 -7
- warp/tests/test_atomic.py +181 -2
- warp/tests/test_builtins_resolution.py +38 -38
- warp/tests/test_codegen.py +24 -3
- warp/tests/test_examples.py +16 -6
- warp/tests/test_fem.py +93 -14
- warp/tests/test_func.py +1 -1
- warp/tests/test_mat.py +416 -119
- warp/tests/test_quat.py +321 -137
- warp/tests/test_struct.py +116 -0
- warp/tests/test_vec.py +320 -174
- warp/tests/tile/test_tile.py +27 -0
- warp/tests/tile/test_tile_load.py +124 -0
- warp/tests/unittest_suites.py +2 -5
- warp/types.py +107 -9
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.2.dist-info}/METADATA +41 -19
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.2.dist-info}/RECORD +60 -57
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.2.dist-info}/WHEEL +1 -1
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.2.dist-info}/licenses/LICENSE.md +0 -26
- {warp_lang-1.7.0.dist-info → warp_lang-1.7.2.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)
|
warp/tests/sim/test_model.py
CHANGED
|
@@ -150,7 +150,9 @@ class TestModel(unittest.TestCase):
|
|
|
150
150
|
assert builder.joint_count == 8
|
|
151
151
|
assert builder.body_count == 8
|
|
152
152
|
assert builder.articulation_count == 3
|
|
153
|
-
add_three_cubes(builder, parent_body=b4)
|
|
153
|
+
lb = add_three_cubes(builder, parent_body=b4)
|
|
154
|
+
# add shape with no mass (e.g. visual only shape)
|
|
155
|
+
builder.add_shape_sphere(lb, density=0.0)
|
|
154
156
|
|
|
155
157
|
builder.collapse_fixed_joints()
|
|
156
158
|
|
|
@@ -158,8 +160,8 @@ class TestModel(unittest.TestCase):
|
|
|
158
160
|
assert builder.articulation_count == 2
|
|
159
161
|
assert builder.articulation_start == [0, 1]
|
|
160
162
|
assert builder.joint_type == [wp.sim.JOINT_REVOLUTE, wp.sim.JOINT_FREE]
|
|
161
|
-
assert builder.shape_count ==
|
|
162
|
-
assert builder.shape_body == [-1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1]
|
|
163
|
+
assert builder.shape_count == 12
|
|
164
|
+
assert builder.shape_body == [-1, -1, -1, -1, -1, -1, 0, 1, 1, 1, 1, 1]
|
|
163
165
|
assert builder.body_count == 2
|
|
164
166
|
assert builder.body_com[0] == wp.vec3(1.0, 2.0, 3.0)
|
|
165
167
|
assert builder.body_com[1] == wp.vec3(0.25, 0.25, 0.25)
|