warp-lang 1.9.0__py3-none-win_amd64.whl → 1.10.0rc2__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/__init__.py +301 -287
- warp/__init__.pyi +2220 -313
- warp/_src/__init__.py +14 -0
- warp/_src/autograd.py +1075 -0
- warp/_src/build.py +618 -0
- warp/_src/build_dll.py +640 -0
- warp/{builtins.py → _src/builtins.py} +1497 -226
- warp/_src/codegen.py +4359 -0
- warp/{config.py → _src/config.py} +178 -169
- warp/_src/constants.py +57 -0
- warp/_src/context.py +8294 -0
- warp/_src/dlpack.py +462 -0
- warp/_src/fabric.py +355 -0
- warp/_src/fem/__init__.py +14 -0
- warp/_src/fem/adaptivity.py +508 -0
- warp/_src/fem/cache.py +687 -0
- warp/_src/fem/dirichlet.py +188 -0
- warp/{fem → _src/fem}/domain.py +40 -30
- warp/_src/fem/field/__init__.py +131 -0
- warp/_src/fem/field/field.py +701 -0
- warp/{fem → _src/fem}/field/nodal_field.py +30 -15
- warp/{fem → _src/fem}/field/restriction.py +1 -1
- warp/{fem → _src/fem}/field/virtual.py +53 -27
- warp/_src/fem/geometry/__init__.py +32 -0
- warp/{fem → _src/fem}/geometry/adaptive_nanogrid.py +77 -163
- warp/_src/fem/geometry/closest_point.py +97 -0
- warp/{fem → _src/fem}/geometry/deformed_geometry.py +14 -22
- warp/{fem → _src/fem}/geometry/element.py +32 -10
- warp/{fem → _src/fem}/geometry/geometry.py +48 -20
- warp/{fem → _src/fem}/geometry/grid_2d.py +12 -23
- warp/{fem → _src/fem}/geometry/grid_3d.py +12 -23
- warp/{fem → _src/fem}/geometry/hexmesh.py +40 -63
- warp/{fem → _src/fem}/geometry/nanogrid.py +255 -248
- warp/{fem → _src/fem}/geometry/partition.py +121 -63
- warp/{fem → _src/fem}/geometry/quadmesh.py +26 -45
- warp/{fem → _src/fem}/geometry/tetmesh.py +40 -63
- warp/{fem → _src/fem}/geometry/trimesh.py +26 -45
- warp/{fem → _src/fem}/integrate.py +164 -158
- warp/_src/fem/linalg.py +383 -0
- warp/_src/fem/operator.py +396 -0
- warp/_src/fem/polynomial.py +229 -0
- warp/{fem → _src/fem}/quadrature/pic_quadrature.py +15 -20
- warp/{fem → _src/fem}/quadrature/quadrature.py +95 -47
- warp/_src/fem/space/__init__.py +248 -0
- warp/{fem → _src/fem}/space/basis_function_space.py +20 -11
- warp/_src/fem/space/basis_space.py +679 -0
- warp/{fem → _src/fem}/space/dof_mapper.py +3 -3
- warp/{fem → _src/fem}/space/function_space.py +14 -13
- warp/{fem → _src/fem}/space/grid_2d_function_space.py +4 -7
- warp/{fem → _src/fem}/space/grid_3d_function_space.py +4 -4
- warp/{fem → _src/fem}/space/hexmesh_function_space.py +4 -10
- warp/{fem → _src/fem}/space/nanogrid_function_space.py +3 -9
- warp/{fem → _src/fem}/space/partition.py +117 -60
- warp/{fem → _src/fem}/space/quadmesh_function_space.py +4 -10
- warp/{fem → _src/fem}/space/restriction.py +66 -33
- warp/_src/fem/space/shape/__init__.py +152 -0
- warp/{fem → _src/fem}/space/shape/cube_shape_function.py +9 -9
- warp/{fem → _src/fem}/space/shape/shape_function.py +8 -9
- warp/{fem → _src/fem}/space/shape/square_shape_function.py +6 -6
- warp/{fem → _src/fem}/space/shape/tet_shape_function.py +3 -3
- warp/{fem → _src/fem}/space/shape/triangle_shape_function.py +3 -3
- warp/{fem → _src/fem}/space/tetmesh_function_space.py +3 -9
- warp/_src/fem/space/topology.py +459 -0
- warp/{fem → _src/fem}/space/trimesh_function_space.py +3 -9
- warp/_src/fem/types.py +112 -0
- warp/_src/fem/utils.py +486 -0
- warp/_src/jax.py +186 -0
- warp/_src/jax_experimental/__init__.py +14 -0
- warp/_src/jax_experimental/custom_call.py +387 -0
- warp/_src/jax_experimental/ffi.py +1284 -0
- warp/_src/jax_experimental/xla_ffi.py +656 -0
- warp/_src/marching_cubes.py +708 -0
- warp/_src/math.py +414 -0
- warp/_src/optim/__init__.py +14 -0
- warp/_src/optim/adam.py +163 -0
- warp/_src/optim/linear.py +1606 -0
- warp/_src/optim/sgd.py +112 -0
- warp/_src/paddle.py +406 -0
- warp/_src/render/__init__.py +14 -0
- warp/_src/render/imgui_manager.py +289 -0
- warp/_src/render/render_opengl.py +3636 -0
- warp/_src/render/render_usd.py +937 -0
- warp/_src/render/utils.py +160 -0
- warp/_src/sparse.py +2716 -0
- warp/_src/tape.py +1206 -0
- warp/{thirdparty → _src/thirdparty}/unittest_parallel.py +9 -2
- warp/_src/torch.py +391 -0
- warp/_src/types.py +5870 -0
- warp/_src/utils.py +1693 -0
- warp/autograd.py +12 -1054
- warp/bin/warp-clang.dll +0 -0
- warp/bin/warp.dll +0 -0
- warp/build.py +8 -588
- warp/build_dll.py +6 -471
- warp/codegen.py +6 -4246
- warp/constants.py +6 -39
- warp/context.py +12 -7851
- warp/dlpack.py +6 -444
- warp/examples/distributed/example_jacobi_mpi.py +4 -5
- warp/examples/fem/example_adaptive_grid.py +1 -1
- warp/examples/fem/example_apic_fluid.py +1 -1
- warp/examples/fem/example_burgers.py +8 -8
- warp/examples/fem/example_diffusion.py +1 -1
- warp/examples/fem/example_distortion_energy.py +1 -1
- warp/examples/fem/example_mixed_elasticity.py +2 -2
- warp/examples/fem/example_navier_stokes.py +1 -1
- warp/examples/fem/example_nonconforming_contact.py +7 -7
- warp/examples/fem/example_stokes.py +1 -1
- warp/examples/fem/example_stokes_transfer.py +1 -1
- warp/examples/fem/utils.py +2 -2
- warp/examples/interop/example_jax_callable.py +1 -1
- warp/examples/interop/example_jax_ffi_callback.py +1 -1
- warp/examples/interop/example_jax_kernel.py +3 -2
- warp/examples/tile/example_tile_mcgp.py +191 -0
- warp/fabric.py +6 -337
- warp/fem/__init__.py +159 -97
- warp/fem/adaptivity.py +7 -489
- warp/fem/cache.py +9 -648
- warp/fem/dirichlet.py +6 -184
- warp/fem/field/__init__.py +8 -109
- warp/fem/field/field.py +7 -652
- warp/fem/geometry/__init__.py +7 -18
- warp/fem/geometry/closest_point.py +11 -77
- warp/fem/linalg.py +18 -366
- warp/fem/operator.py +11 -369
- warp/fem/polynomial.py +9 -209
- warp/fem/space/__init__.py +5 -211
- warp/fem/space/basis_space.py +6 -662
- warp/fem/space/shape/__init__.py +41 -118
- warp/fem/space/topology.py +6 -437
- warp/fem/types.py +6 -81
- warp/fem/utils.py +11 -444
- warp/jax.py +8 -165
- warp/jax_experimental/__init__.py +14 -1
- warp/jax_experimental/custom_call.py +8 -342
- warp/jax_experimental/ffi.py +17 -853
- warp/jax_experimental/xla_ffi.py +5 -596
- warp/marching_cubes.py +5 -689
- warp/math.py +16 -393
- warp/native/array.h +385 -37
- warp/native/builtin.h +316 -39
- warp/native/bvh.cpp +43 -9
- warp/native/bvh.cu +62 -27
- warp/native/bvh.h +310 -309
- warp/native/clang/clang.cpp +102 -97
- warp/native/coloring.cpp +0 -1
- warp/native/crt.h +208 -0
- warp/native/exports.h +156 -0
- warp/native/hashgrid.cu +2 -0
- warp/native/intersect.h +24 -1
- warp/native/intersect_tri.h +44 -35
- warp/native/mat.h +1456 -276
- warp/native/mesh.cpp +4 -4
- warp/native/mesh.cu +4 -2
- warp/native/mesh.h +176 -61
- warp/native/quat.h +0 -52
- warp/native/scan.cu +2 -0
- warp/native/sort.cu +22 -13
- warp/native/sort.h +2 -0
- warp/native/sparse.cu +7 -3
- warp/native/spatial.h +12 -0
- warp/native/tile.h +837 -70
- warp/native/tile_radix_sort.h +1 -1
- warp/native/tile_reduce.h +394 -46
- warp/native/tile_scan.h +4 -4
- warp/native/vec.h +469 -53
- warp/native/version.h +23 -0
- warp/native/volume.cpp +1 -1
- warp/native/volume.cu +1 -0
- warp/native/volume.h +1 -1
- warp/native/volume_builder.cu +2 -0
- warp/native/warp.cpp +60 -32
- warp/native/warp.cu +313 -201
- warp/native/warp.h +14 -11
- warp/optim/__init__.py +6 -3
- warp/optim/adam.py +6 -145
- warp/optim/linear.py +14 -1585
- warp/optim/sgd.py +6 -94
- warp/paddle.py +6 -388
- warp/render/__init__.py +8 -4
- warp/render/imgui_manager.py +7 -267
- warp/render/render_opengl.py +6 -3616
- warp/render/render_usd.py +6 -918
- warp/render/utils.py +6 -142
- warp/sparse.py +37 -2563
- warp/tape.py +6 -1188
- warp/tests/__main__.py +1 -1
- warp/tests/cuda/test_async.py +4 -4
- warp/tests/cuda/test_conditional_captures.py +1 -1
- warp/tests/cuda/test_multigpu.py +1 -1
- warp/tests/cuda/test_streams.py +58 -1
- warp/tests/geometry/test_bvh.py +157 -22
- warp/tests/geometry/test_hash_grid.py +38 -0
- warp/tests/geometry/test_marching_cubes.py +0 -1
- warp/tests/geometry/test_mesh.py +5 -3
- warp/tests/geometry/test_mesh_query_aabb.py +5 -12
- warp/tests/geometry/test_mesh_query_point.py +5 -2
- warp/tests/geometry/test_mesh_query_ray.py +15 -3
- warp/tests/geometry/test_volume_write.py +5 -5
- warp/tests/interop/test_dlpack.py +14 -14
- warp/tests/interop/test_jax.py +1382 -79
- warp/tests/interop/test_paddle.py +1 -1
- warp/tests/test_adam.py +0 -1
- warp/tests/test_arithmetic.py +9 -9
- warp/tests/test_array.py +529 -100
- warp/tests/test_array_reduce.py +3 -3
- warp/tests/test_atomic.py +12 -8
- warp/tests/test_atomic_bitwise.py +209 -0
- warp/tests/test_atomic_cas.py +4 -4
- warp/tests/test_bool.py +2 -2
- warp/tests/test_builtins_resolution.py +5 -571
- warp/tests/test_codegen.py +34 -15
- warp/tests/test_conditional.py +1 -1
- warp/tests/test_context.py +6 -6
- warp/tests/test_copy.py +242 -161
- warp/tests/test_ctypes.py +3 -3
- warp/tests/test_devices.py +24 -2
- warp/tests/test_examples.py +16 -84
- warp/tests/test_fabricarray.py +35 -35
- warp/tests/test_fast_math.py +0 -2
- warp/tests/test_fem.py +60 -14
- warp/tests/test_fixedarray.py +3 -3
- warp/tests/test_func.py +8 -5
- warp/tests/test_generics.py +1 -1
- warp/tests/test_indexedarray.py +24 -24
- warp/tests/test_intersect.py +39 -9
- warp/tests/test_large.py +1 -1
- warp/tests/test_lerp.py +3 -1
- warp/tests/test_linear_solvers.py +1 -1
- warp/tests/test_map.py +49 -4
- warp/tests/test_mat.py +52 -62
- warp/tests/test_mat_constructors.py +4 -5
- warp/tests/test_mat_lite.py +1 -1
- warp/tests/test_mat_scalar_ops.py +121 -121
- warp/tests/test_math.py +34 -0
- warp/tests/test_module_aot.py +4 -4
- warp/tests/test_modules_lite.py +28 -2
- warp/tests/test_print.py +11 -11
- warp/tests/test_quat.py +93 -58
- warp/tests/test_runlength_encode.py +1 -1
- warp/tests/test_scalar_ops.py +38 -10
- warp/tests/test_smoothstep.py +1 -1
- warp/tests/test_sparse.py +126 -15
- warp/tests/test_spatial.py +105 -87
- warp/tests/test_special_values.py +6 -6
- warp/tests/test_static.py +7 -7
- warp/tests/test_struct.py +13 -2
- warp/tests/test_triangle_closest_point.py +48 -1
- warp/tests/test_tuple.py +96 -0
- warp/tests/test_types.py +82 -9
- warp/tests/test_utils.py +52 -52
- warp/tests/test_vec.py +29 -29
- warp/tests/test_vec_constructors.py +5 -5
- warp/tests/test_vec_scalar_ops.py +97 -97
- warp/tests/test_version.py +75 -0
- warp/tests/tile/test_tile.py +239 -0
- warp/tests/tile/test_tile_atomic_bitwise.py +403 -0
- warp/tests/tile/test_tile_cholesky.py +7 -4
- warp/tests/tile/test_tile_load.py +26 -2
- warp/tests/tile/test_tile_mathdx.py +3 -3
- warp/tests/tile/test_tile_matmul.py +1 -1
- warp/tests/tile/test_tile_mlp.py +2 -4
- warp/tests/tile/test_tile_reduce.py +214 -13
- warp/tests/unittest_suites.py +6 -14
- warp/tests/unittest_utils.py +10 -9
- warp/tests/walkthrough_debug.py +3 -1
- warp/torch.py +6 -373
- warp/types.py +29 -5750
- warp/utils.py +10 -1659
- {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/METADATA +47 -103
- warp_lang-1.10.0rc2.dist-info/RECORD +468 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/Gaia-LICENSE.txt +6 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/appdirs-LICENSE.txt +22 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/asset_pixel_jpg-LICENSE.txt +3 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/cuda-LICENSE.txt +1582 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/dlpack-LICENSE.txt +201 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/fp16-LICENSE.txt +28 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/libmathdx-LICENSE.txt +220 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/llvm-LICENSE.txt +279 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/moller-LICENSE.txt +16 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/nanovdb-LICENSE.txt +2 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/nvrtc-LICENSE.txt +1592 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/svd-LICENSE.txt +23 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/unittest_parallel-LICENSE.txt +21 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/usd-LICENSE.txt +213 -0
- warp_lang-1.10.0rc2.dist-info/licenses/licenses/windingnumber-LICENSE.txt +21 -0
- warp/examples/assets/cartpole.urdf +0 -110
- warp/examples/assets/crazyflie.usd +0 -0
- warp/examples/assets/nv_ant.xml +0 -92
- warp/examples/assets/nv_humanoid.xml +0 -183
- warp/examples/assets/quadruped.urdf +0 -268
- warp/examples/optim/example_bounce.py +0 -266
- warp/examples/optim/example_cloth_throw.py +0 -228
- warp/examples/optim/example_drone.py +0 -870
- warp/examples/optim/example_inverse_kinematics.py +0 -182
- warp/examples/optim/example_inverse_kinematics_torch.py +0 -191
- warp/examples/optim/example_softbody_properties.py +0 -400
- warp/examples/optim/example_spring_cage.py +0 -245
- warp/examples/optim/example_trajectory.py +0 -227
- warp/examples/sim/example_cartpole.py +0 -143
- warp/examples/sim/example_cloth.py +0 -225
- warp/examples/sim/example_cloth_self_contact.py +0 -316
- warp/examples/sim/example_granular.py +0 -130
- warp/examples/sim/example_granular_collision_sdf.py +0 -202
- warp/examples/sim/example_jacobian_ik.py +0 -244
- warp/examples/sim/example_particle_chain.py +0 -124
- warp/examples/sim/example_quadruped.py +0 -203
- warp/examples/sim/example_rigid_chain.py +0 -203
- warp/examples/sim/example_rigid_contact.py +0 -195
- warp/examples/sim/example_rigid_force.py +0 -133
- warp/examples/sim/example_rigid_gyroscopic.py +0 -115
- warp/examples/sim/example_rigid_soft_contact.py +0 -140
- warp/examples/sim/example_soft_body.py +0 -196
- warp/examples/tile/example_tile_walker.py +0 -327
- warp/sim/__init__.py +0 -74
- warp/sim/articulation.py +0 -793
- warp/sim/collide.py +0 -2570
- warp/sim/graph_coloring.py +0 -307
- warp/sim/import_mjcf.py +0 -791
- warp/sim/import_snu.py +0 -227
- warp/sim/import_urdf.py +0 -579
- warp/sim/import_usd.py +0 -898
- warp/sim/inertia.py +0 -357
- warp/sim/integrator.py +0 -245
- warp/sim/integrator_euler.py +0 -2000
- warp/sim/integrator_featherstone.py +0 -2101
- warp/sim/integrator_vbd.py +0 -2487
- warp/sim/integrator_xpbd.py +0 -3295
- warp/sim/model.py +0 -4821
- warp/sim/particles.py +0 -121
- warp/sim/render.py +0 -431
- warp/sim/utils.py +0 -431
- warp/tests/sim/disabled_kinematics.py +0 -244
- warp/tests/sim/test_cloth.py +0 -863
- warp/tests/sim/test_collision.py +0 -743
- warp/tests/sim/test_coloring.py +0 -347
- warp/tests/sim/test_inertia.py +0 -161
- warp/tests/sim/test_model.py +0 -226
- warp/tests/sim/test_sim_grad.py +0 -287
- warp/tests/sim/test_sim_grad_bounce_linear.py +0 -212
- warp/tests/sim/test_sim_kinematics.py +0 -98
- warp/thirdparty/__init__.py +0 -0
- warp_lang-1.9.0.dist-info/RECORD +0 -456
- /warp/{fem → _src/fem}/quadrature/__init__.py +0 -0
- /warp/{tests/sim → _src/thirdparty}/__init__.py +0 -0
- /warp/{thirdparty → _src/thirdparty}/appdirs.py +0 -0
- /warp/{thirdparty → _src/thirdparty}/dlpack.py +0 -0
- {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/WHEEL +0 -0
- {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/licenses/LICENSE.md +0 -0
- {warp_lang-1.9.0.dist-info → warp_lang-1.10.0rc2.dist-info}/top_level.txt +0 -0
warp/sim/collide.py
DELETED
|
@@ -1,2570 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2022 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
|
-
"""
|
|
17
|
-
Collision handling functions and kernels.
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
from typing import Optional
|
|
21
|
-
|
|
22
|
-
import numpy as np
|
|
23
|
-
|
|
24
|
-
import warp as wp
|
|
25
|
-
from warp.sim.model import Model, State
|
|
26
|
-
|
|
27
|
-
from .model import PARTICLE_FLAG_ACTIVE, ModelShapeGeometry
|
|
28
|
-
|
|
29
|
-
# types of triangle's closest point to a point
|
|
30
|
-
TRI_CONTACT_FEATURE_VERTEX_A = wp.constant(0)
|
|
31
|
-
TRI_CONTACT_FEATURE_VERTEX_B = wp.constant(1)
|
|
32
|
-
TRI_CONTACT_FEATURE_VERTEX_C = wp.constant(2)
|
|
33
|
-
TRI_CONTACT_FEATURE_EDGE_AB = wp.constant(3)
|
|
34
|
-
TRI_CONTACT_FEATURE_EDGE_AC = wp.constant(4)
|
|
35
|
-
TRI_CONTACT_FEATURE_EDGE_BC = wp.constant(5)
|
|
36
|
-
TRI_CONTACT_FEATURE_FACE_INTERIOR = wp.constant(6)
|
|
37
|
-
|
|
38
|
-
# constants used to access TriMeshCollisionDetector.resize_flags
|
|
39
|
-
VERTEX_COLLISION_BUFFER_OVERFLOW_INDEX = wp.constant(0)
|
|
40
|
-
TRI_COLLISION_BUFFER_OVERFLOW_INDEX = wp.constant(1)
|
|
41
|
-
EDGE_COLLISION_BUFFER_OVERFLOW_INDEX = wp.constant(2)
|
|
42
|
-
TRI_TRI_COLLISION_BUFFER_OVERFLOW_INDEX = wp.constant(3)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@wp.func
|
|
46
|
-
def build_orthonormal_basis(n: wp.vec3):
|
|
47
|
-
"""
|
|
48
|
-
Builds an orthonormal basis given a normal vector `n`. Return the two axes that are perpendicular to `n`.
|
|
49
|
-
|
|
50
|
-
:param n: A 3D vector (list or array-like) representing the normal vector
|
|
51
|
-
"""
|
|
52
|
-
b1 = wp.vec3()
|
|
53
|
-
b2 = wp.vec3()
|
|
54
|
-
if n[2] < 0.0:
|
|
55
|
-
a = 1.0 / (1.0 - n[2])
|
|
56
|
-
b = n[0] * n[1] * a
|
|
57
|
-
b1[0] = 1.0 - n[0] * n[0] * a
|
|
58
|
-
b1[1] = -b
|
|
59
|
-
b1[2] = n[0]
|
|
60
|
-
|
|
61
|
-
b2[0] = b
|
|
62
|
-
b2[1] = n[1] * n[1] * a - 1.0
|
|
63
|
-
b2[2] = -n[1]
|
|
64
|
-
else:
|
|
65
|
-
a = 1.0 / (1.0 + n[2])
|
|
66
|
-
b = -n[0] * n[1] * a
|
|
67
|
-
b1[0] = 1.0 - n[0] * n[0] * a
|
|
68
|
-
b1[1] = b
|
|
69
|
-
b1[2] = -n[0]
|
|
70
|
-
|
|
71
|
-
b2[0] = b
|
|
72
|
-
b2[1] = 1.0 - n[1] * n[1] * a
|
|
73
|
-
b2[2] = -n[1]
|
|
74
|
-
|
|
75
|
-
return b1, b2
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
@wp.func
|
|
79
|
-
def triangle_closest_point_barycentric(a: wp.vec3, b: wp.vec3, c: wp.vec3, p: wp.vec3):
|
|
80
|
-
ab = b - a
|
|
81
|
-
ac = c - a
|
|
82
|
-
ap = p - a
|
|
83
|
-
|
|
84
|
-
d1 = wp.dot(ab, ap)
|
|
85
|
-
d2 = wp.dot(ac, ap)
|
|
86
|
-
|
|
87
|
-
if d1 <= 0.0 and d2 <= 0.0:
|
|
88
|
-
return wp.vec3(1.0, 0.0, 0.0)
|
|
89
|
-
|
|
90
|
-
bp = p - b
|
|
91
|
-
d3 = wp.dot(ab, bp)
|
|
92
|
-
d4 = wp.dot(ac, bp)
|
|
93
|
-
|
|
94
|
-
if d3 >= 0.0 and d4 <= d3:
|
|
95
|
-
return wp.vec3(0.0, 1.0, 0.0)
|
|
96
|
-
|
|
97
|
-
vc = d1 * d4 - d3 * d2
|
|
98
|
-
v = d1 / (d1 - d3)
|
|
99
|
-
if vc <= 0.0 and d1 >= 0.0 and d3 <= 0.0:
|
|
100
|
-
return wp.vec3(1.0 - v, v, 0.0)
|
|
101
|
-
|
|
102
|
-
cp = p - c
|
|
103
|
-
d5 = wp.dot(ab, cp)
|
|
104
|
-
d6 = wp.dot(ac, cp)
|
|
105
|
-
|
|
106
|
-
if d6 >= 0.0 and d5 <= d6:
|
|
107
|
-
return wp.vec3(0.0, 0.0, 1.0)
|
|
108
|
-
|
|
109
|
-
vb = d5 * d2 - d1 * d6
|
|
110
|
-
w = d2 / (d2 - d6)
|
|
111
|
-
if vb <= 0.0 and d2 >= 0.0 and d6 <= 0.0:
|
|
112
|
-
return wp.vec3(1.0 - w, 0.0, w)
|
|
113
|
-
|
|
114
|
-
va = d3 * d6 - d5 * d4
|
|
115
|
-
w = (d4 - d3) / ((d4 - d3) + (d5 - d6))
|
|
116
|
-
if va <= 0.0 and (d4 - d3) >= 0.0 and (d5 - d6) >= 0.0:
|
|
117
|
-
return wp.vec3(0.0, 1.0 - w, w)
|
|
118
|
-
|
|
119
|
-
denom = 1.0 / (va + vb + vc)
|
|
120
|
-
v = vb * denom
|
|
121
|
-
w = vc * denom
|
|
122
|
-
|
|
123
|
-
return wp.vec3(1.0 - v - w, v, w)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@wp.func
|
|
127
|
-
def triangle_closest_point(a: wp.vec3, b: wp.vec3, c: wp.vec3, p: wp.vec3):
|
|
128
|
-
"""
|
|
129
|
-
feature_type type:
|
|
130
|
-
TRI_CONTACT_FEATURE_VERTEX_A
|
|
131
|
-
TRI_CONTACT_FEATURE_VERTEX_B
|
|
132
|
-
TRI_CONTACT_FEATURE_VERTEX_C
|
|
133
|
-
TRI_CONTACT_FEATURE_EDGE_AB : at edge A-B
|
|
134
|
-
TRI_CONTACT_FEATURE_EDGE_AC : at edge A-C
|
|
135
|
-
TRI_CONTACT_FEATURE_EDGE_BC : at edge B-C
|
|
136
|
-
TRI_CONTACT_FEATURE_FACE_INTERIOR
|
|
137
|
-
"""
|
|
138
|
-
ab = b - a
|
|
139
|
-
ac = c - a
|
|
140
|
-
ap = p - a
|
|
141
|
-
|
|
142
|
-
d1 = wp.dot(ab, ap)
|
|
143
|
-
d2 = wp.dot(ac, ap)
|
|
144
|
-
if d1 <= 0.0 and d2 <= 0.0:
|
|
145
|
-
feature_type = TRI_CONTACT_FEATURE_VERTEX_A
|
|
146
|
-
bary = wp.vec3(1.0, 0.0, 0.0)
|
|
147
|
-
return a, bary, feature_type
|
|
148
|
-
|
|
149
|
-
bp = p - b
|
|
150
|
-
d3 = wp.dot(ab, bp)
|
|
151
|
-
d4 = wp.dot(ac, bp)
|
|
152
|
-
if d3 >= 0.0 and d4 <= d3:
|
|
153
|
-
feature_type = TRI_CONTACT_FEATURE_VERTEX_B
|
|
154
|
-
bary = wp.vec3(0.0, 1.0, 0.0)
|
|
155
|
-
return b, bary, feature_type
|
|
156
|
-
|
|
157
|
-
cp = p - c
|
|
158
|
-
d5 = wp.dot(ab, cp)
|
|
159
|
-
d6 = wp.dot(ac, cp)
|
|
160
|
-
if d6 >= 0.0 and d5 <= d6:
|
|
161
|
-
feature_type = TRI_CONTACT_FEATURE_VERTEX_C
|
|
162
|
-
bary = wp.vec3(0.0, 0.0, 1.0)
|
|
163
|
-
return c, bary, feature_type
|
|
164
|
-
|
|
165
|
-
vc = d1 * d4 - d3 * d2
|
|
166
|
-
if vc <= 0.0 and d1 >= 0.0 and d3 <= 0.0:
|
|
167
|
-
v = d1 / (d1 - d3)
|
|
168
|
-
feature_type = TRI_CONTACT_FEATURE_EDGE_AB
|
|
169
|
-
bary = wp.vec3(1.0 - v, v, 0.0)
|
|
170
|
-
return a + v * ab, bary, feature_type
|
|
171
|
-
|
|
172
|
-
vb = d5 * d2 - d1 * d6
|
|
173
|
-
if vb <= 0.0 and d2 >= 0.0 and d6 <= 0.0:
|
|
174
|
-
v = d2 / (d2 - d6)
|
|
175
|
-
feature_type = TRI_CONTACT_FEATURE_EDGE_AC
|
|
176
|
-
bary = wp.vec3(1.0 - v, 0.0, v)
|
|
177
|
-
return a + v * ac, bary, feature_type
|
|
178
|
-
|
|
179
|
-
va = d3 * d6 - d5 * d4
|
|
180
|
-
if va <= 0.0 and (d4 - d3) >= 0.0 and (d5 - d6) >= 0.0:
|
|
181
|
-
v = (d4 - d3) / ((d4 - d3) + (d5 - d6))
|
|
182
|
-
feature_type = TRI_CONTACT_FEATURE_EDGE_BC
|
|
183
|
-
bary = wp.vec3(0.0, 1.0 - v, v)
|
|
184
|
-
return b + v * (c - b), bary, feature_type
|
|
185
|
-
|
|
186
|
-
denom = 1.0 / (va + vb + vc)
|
|
187
|
-
v = vb * denom
|
|
188
|
-
w = vc * denom
|
|
189
|
-
feature_type = TRI_CONTACT_FEATURE_FACE_INTERIOR
|
|
190
|
-
bary = wp.vec3(1.0 - v - w, v, w)
|
|
191
|
-
return a + v * ab + w * ac, bary, feature_type
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
@wp.func
|
|
195
|
-
def sphere_sdf(center: wp.vec3, radius: float, p: wp.vec3):
|
|
196
|
-
return wp.length(p - center) - radius
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
@wp.func
|
|
200
|
-
def sphere_sdf_grad(center: wp.vec3, radius: float, p: wp.vec3):
|
|
201
|
-
return wp.normalize(p - center)
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
@wp.func
|
|
205
|
-
def box_sdf(upper: wp.vec3, p: wp.vec3):
|
|
206
|
-
# adapted from https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm
|
|
207
|
-
qx = abs(p[0]) - upper[0]
|
|
208
|
-
qy = abs(p[1]) - upper[1]
|
|
209
|
-
qz = abs(p[2]) - upper[2]
|
|
210
|
-
|
|
211
|
-
e = wp.vec3(wp.max(qx, 0.0), wp.max(qy, 0.0), wp.max(qz, 0.0))
|
|
212
|
-
|
|
213
|
-
return wp.length(e) + wp.min(wp.max(qx, wp.max(qy, qz)), 0.0)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
@wp.func
|
|
217
|
-
def box_sdf_grad(upper: wp.vec3, p: wp.vec3):
|
|
218
|
-
qx = abs(p[0]) - upper[0]
|
|
219
|
-
qy = abs(p[1]) - upper[1]
|
|
220
|
-
qz = abs(p[2]) - upper[2]
|
|
221
|
-
|
|
222
|
-
# exterior case
|
|
223
|
-
if qx > 0.0 or qy > 0.0 or qz > 0.0:
|
|
224
|
-
x = wp.clamp(p[0], -upper[0], upper[0])
|
|
225
|
-
y = wp.clamp(p[1], -upper[1], upper[1])
|
|
226
|
-
z = wp.clamp(p[2], -upper[2], upper[2])
|
|
227
|
-
|
|
228
|
-
return wp.normalize(p - wp.vec3(x, y, z))
|
|
229
|
-
|
|
230
|
-
sx = wp.sign(p[0])
|
|
231
|
-
sy = wp.sign(p[1])
|
|
232
|
-
sz = wp.sign(p[2])
|
|
233
|
-
|
|
234
|
-
# x projection
|
|
235
|
-
if (qx > qy and qx > qz) or (qy == 0.0 and qz == 0.0):
|
|
236
|
-
return wp.vec3(sx, 0.0, 0.0)
|
|
237
|
-
|
|
238
|
-
# y projection
|
|
239
|
-
if (qy > qx and qy > qz) or (qx == 0.0 and qz == 0.0):
|
|
240
|
-
return wp.vec3(0.0, sy, 0.0)
|
|
241
|
-
|
|
242
|
-
# z projection
|
|
243
|
-
return wp.vec3(0.0, 0.0, sz)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
@wp.func
|
|
247
|
-
def capsule_sdf(radius: float, half_height: float, p: wp.vec3):
|
|
248
|
-
if p[1] > half_height:
|
|
249
|
-
return wp.length(wp.vec3(p[0], p[1] - half_height, p[2])) - radius
|
|
250
|
-
|
|
251
|
-
if p[1] < -half_height:
|
|
252
|
-
return wp.length(wp.vec3(p[0], p[1] + half_height, p[2])) - radius
|
|
253
|
-
|
|
254
|
-
return wp.length(wp.vec3(p[0], 0.0, p[2])) - radius
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
@wp.func
|
|
258
|
-
def capsule_sdf_grad(radius: float, half_height: float, p: wp.vec3):
|
|
259
|
-
if p[1] > half_height:
|
|
260
|
-
return wp.normalize(wp.vec3(p[0], p[1] - half_height, p[2]))
|
|
261
|
-
|
|
262
|
-
if p[1] < -half_height:
|
|
263
|
-
return wp.normalize(wp.vec3(p[0], p[1] + half_height, p[2]))
|
|
264
|
-
|
|
265
|
-
return wp.normalize(wp.vec3(p[0], 0.0, p[2]))
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
@wp.func
|
|
269
|
-
def cylinder_sdf(radius: float, half_height: float, p: wp.vec3):
|
|
270
|
-
dx = wp.length(wp.vec3(p[0], 0.0, p[2])) - radius
|
|
271
|
-
dy = wp.abs(p[1]) - half_height
|
|
272
|
-
return wp.min(wp.max(dx, dy), 0.0) + wp.length(wp.vec2(wp.max(dx, 0.0), wp.max(dy, 0.0)))
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
@wp.func
|
|
276
|
-
def cylinder_sdf_grad(radius: float, half_height: float, p: wp.vec3):
|
|
277
|
-
dx = wp.length(wp.vec3(p[0], 0.0, p[2])) - radius
|
|
278
|
-
dy = wp.abs(p[1]) - half_height
|
|
279
|
-
if dx > dy:
|
|
280
|
-
return wp.normalize(wp.vec3(p[0], 0.0, p[2]))
|
|
281
|
-
return wp.vec3(0.0, wp.sign(p[1]), 0.0)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
@wp.func
|
|
285
|
-
def cone_sdf(radius: float, half_height: float, p: wp.vec3):
|
|
286
|
-
dx = wp.length(wp.vec3(p[0], 0.0, p[2])) - radius * (p[1] + half_height) / (2.0 * half_height)
|
|
287
|
-
dy = wp.abs(p[1]) - half_height
|
|
288
|
-
return wp.min(wp.max(dx, dy), 0.0) + wp.length(wp.vec2(wp.max(dx, 0.0), wp.max(dy, 0.0)))
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
@wp.func
|
|
292
|
-
def cone_sdf_grad(radius: float, half_height: float, p: wp.vec3):
|
|
293
|
-
dx = wp.length(wp.vec3(p[0], 0.0, p[2])) - radius * (p[1] + half_height) / (2.0 * half_height)
|
|
294
|
-
dy = wp.abs(p[1]) - half_height
|
|
295
|
-
if dy < 0.0 or dx == 0.0:
|
|
296
|
-
return wp.vec3(0.0, wp.sign(p[1]), 0.0)
|
|
297
|
-
return wp.normalize(wp.vec3(p[0], 0.0, p[2])) + wp.vec3(0.0, radius / (2.0 * half_height), 0.0)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
@wp.func
|
|
301
|
-
def plane_sdf(width: float, length: float, p: wp.vec3):
|
|
302
|
-
# SDF for a quad in the xz plane
|
|
303
|
-
if width > 0.0 and length > 0.0:
|
|
304
|
-
d = wp.max(wp.abs(p[0]) - width, wp.abs(p[2]) - length)
|
|
305
|
-
return wp.max(d, wp.abs(p[1]))
|
|
306
|
-
return p[1]
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
@wp.func
|
|
310
|
-
def closest_point_plane(width: float, length: float, point: wp.vec3):
|
|
311
|
-
# projects the point onto the quad in the xz plane (if width and length > 0.0, otherwise the plane is infinite)
|
|
312
|
-
if width > 0.0:
|
|
313
|
-
x = wp.clamp(point[0], -width, width)
|
|
314
|
-
else:
|
|
315
|
-
x = point[0]
|
|
316
|
-
if length > 0.0:
|
|
317
|
-
z = wp.clamp(point[2], -length, length)
|
|
318
|
-
else:
|
|
319
|
-
z = point[2]
|
|
320
|
-
return wp.vec3(x, 0.0, z)
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
@wp.func
|
|
324
|
-
def closest_point_line_segment(a: wp.vec3, b: wp.vec3, point: wp.vec3):
|
|
325
|
-
ab = b - a
|
|
326
|
-
ap = point - a
|
|
327
|
-
t = wp.dot(ap, ab) / wp.dot(ab, ab)
|
|
328
|
-
t = wp.clamp(t, 0.0, 1.0)
|
|
329
|
-
return a + t * ab
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
@wp.func
|
|
333
|
-
def closest_point_box(upper: wp.vec3, point: wp.vec3):
|
|
334
|
-
# closest point to box surface
|
|
335
|
-
x = wp.clamp(point[0], -upper[0], upper[0])
|
|
336
|
-
y = wp.clamp(point[1], -upper[1], upper[1])
|
|
337
|
-
z = wp.clamp(point[2], -upper[2], upper[2])
|
|
338
|
-
if wp.abs(point[0]) <= upper[0] and wp.abs(point[1]) <= upper[1] and wp.abs(point[2]) <= upper[2]:
|
|
339
|
-
# the point is inside, find closest face
|
|
340
|
-
sx = wp.abs(wp.abs(point[0]) - upper[0])
|
|
341
|
-
sy = wp.abs(wp.abs(point[1]) - upper[1])
|
|
342
|
-
sz = wp.abs(wp.abs(point[2]) - upper[2])
|
|
343
|
-
# return closest point on closest side, handle corner cases
|
|
344
|
-
if (sx < sy and sx < sz) or (sy == 0.0 and sz == 0.0):
|
|
345
|
-
x = wp.sign(point[0]) * upper[0]
|
|
346
|
-
elif (sy < sx and sy < sz) or (sx == 0.0 and sz == 0.0):
|
|
347
|
-
y = wp.sign(point[1]) * upper[1]
|
|
348
|
-
else:
|
|
349
|
-
z = wp.sign(point[2]) * upper[2]
|
|
350
|
-
return wp.vec3(x, y, z)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
@wp.func
|
|
354
|
-
def get_box_vertex(point_id: int, upper: wp.vec3):
|
|
355
|
-
# box vertex numbering:
|
|
356
|
-
# 6---7
|
|
357
|
-
# |\ |\ y
|
|
358
|
-
# | 2-+-3 |
|
|
359
|
-
# 4-+-5 | z \|
|
|
360
|
-
# \| \| o---x
|
|
361
|
-
# 0---1
|
|
362
|
-
# get the vertex of the box given its ID (0-7)
|
|
363
|
-
sign_x = float(point_id % 2) * 2.0 - 1.0
|
|
364
|
-
sign_y = float((point_id // 2) % 2) * 2.0 - 1.0
|
|
365
|
-
sign_z = float((point_id // 4) % 2) * 2.0 - 1.0
|
|
366
|
-
return wp.vec3(sign_x * upper[0], sign_y * upper[1], sign_z * upper[2])
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
@wp.func
|
|
370
|
-
def get_box_edge(edge_id: int, upper: wp.vec3):
|
|
371
|
-
# get the edge of the box given its ID (0-11)
|
|
372
|
-
if edge_id < 4:
|
|
373
|
-
# edges along x: 0-1, 2-3, 4-5, 6-7
|
|
374
|
-
i = edge_id * 2
|
|
375
|
-
j = i + 1
|
|
376
|
-
return wp.spatial_vector(get_box_vertex(i, upper), get_box_vertex(j, upper))
|
|
377
|
-
elif edge_id < 8:
|
|
378
|
-
# edges along y: 0-2, 1-3, 4-6, 5-7
|
|
379
|
-
edge_id -= 4
|
|
380
|
-
i = edge_id % 2 + edge_id // 2 * 4
|
|
381
|
-
j = i + 2
|
|
382
|
-
return wp.spatial_vector(get_box_vertex(i, upper), get_box_vertex(j, upper))
|
|
383
|
-
# edges along z: 0-4, 1-5, 2-6, 3-7
|
|
384
|
-
edge_id -= 8
|
|
385
|
-
i = edge_id
|
|
386
|
-
j = i + 4
|
|
387
|
-
return wp.spatial_vector(get_box_vertex(i, upper), get_box_vertex(j, upper))
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
@wp.func
|
|
391
|
-
def get_plane_edge(edge_id: int, plane_width: float, plane_length: float):
|
|
392
|
-
# get the edge of the plane given its ID (0-3)
|
|
393
|
-
p0x = (2.0 * float(edge_id % 2) - 1.0) * plane_width
|
|
394
|
-
p0z = (2.0 * float(edge_id // 2) - 1.0) * plane_length
|
|
395
|
-
if edge_id == 0 or edge_id == 3:
|
|
396
|
-
p1x = p0x
|
|
397
|
-
p1z = -p0z
|
|
398
|
-
else:
|
|
399
|
-
p1x = -p0x
|
|
400
|
-
p1z = p0z
|
|
401
|
-
return wp.spatial_vector(wp.vec3(p0x, 0.0, p0z), wp.vec3(p1x, 0.0, p1z))
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
@wp.func
|
|
405
|
-
def closest_edge_coordinate_box(upper: wp.vec3, edge_a: wp.vec3, edge_b: wp.vec3, max_iter: int):
|
|
406
|
-
# find point on edge closest to box, return its barycentric edge coordinate
|
|
407
|
-
# Golden-section search
|
|
408
|
-
a = float(0.0)
|
|
409
|
-
b = float(1.0)
|
|
410
|
-
h = b - a
|
|
411
|
-
invphi = 0.61803398875 # 1 / phi
|
|
412
|
-
invphi2 = 0.38196601125 # 1 / phi^2
|
|
413
|
-
c = a + invphi2 * h
|
|
414
|
-
d = a + invphi * h
|
|
415
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
416
|
-
yc = box_sdf(upper, query)
|
|
417
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
418
|
-
yd = box_sdf(upper, query)
|
|
419
|
-
|
|
420
|
-
for _k in range(max_iter):
|
|
421
|
-
if yc < yd: # yc > yd to find the maximum
|
|
422
|
-
b = d
|
|
423
|
-
d = c
|
|
424
|
-
yd = yc
|
|
425
|
-
h = invphi * h
|
|
426
|
-
c = a + invphi2 * h
|
|
427
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
428
|
-
yc = box_sdf(upper, query)
|
|
429
|
-
else:
|
|
430
|
-
a = c
|
|
431
|
-
c = d
|
|
432
|
-
yc = yd
|
|
433
|
-
h = invphi * h
|
|
434
|
-
d = a + invphi * h
|
|
435
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
436
|
-
yd = box_sdf(upper, query)
|
|
437
|
-
|
|
438
|
-
if yc < yd:
|
|
439
|
-
return 0.5 * (a + d)
|
|
440
|
-
return 0.5 * (c + b)
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
@wp.func
|
|
444
|
-
def closest_edge_coordinate_plane(
|
|
445
|
-
plane_width: float,
|
|
446
|
-
plane_length: float,
|
|
447
|
-
edge_a: wp.vec3,
|
|
448
|
-
edge_b: wp.vec3,
|
|
449
|
-
max_iter: int,
|
|
450
|
-
):
|
|
451
|
-
# find point on edge closest to plane, return its barycentric edge coordinate
|
|
452
|
-
# Golden-section search
|
|
453
|
-
a = float(0.0)
|
|
454
|
-
b = float(1.0)
|
|
455
|
-
h = b - a
|
|
456
|
-
invphi = 0.61803398875 # 1 / phi
|
|
457
|
-
invphi2 = 0.38196601125 # 1 / phi^2
|
|
458
|
-
c = a + invphi2 * h
|
|
459
|
-
d = a + invphi * h
|
|
460
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
461
|
-
yc = plane_sdf(plane_width, plane_length, query)
|
|
462
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
463
|
-
yd = plane_sdf(plane_width, plane_length, query)
|
|
464
|
-
|
|
465
|
-
for _k in range(max_iter):
|
|
466
|
-
if yc < yd: # yc > yd to find the maximum
|
|
467
|
-
b = d
|
|
468
|
-
d = c
|
|
469
|
-
yd = yc
|
|
470
|
-
h = invphi * h
|
|
471
|
-
c = a + invphi2 * h
|
|
472
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
473
|
-
yc = plane_sdf(plane_width, plane_length, query)
|
|
474
|
-
else:
|
|
475
|
-
a = c
|
|
476
|
-
c = d
|
|
477
|
-
yc = yd
|
|
478
|
-
h = invphi * h
|
|
479
|
-
d = a + invphi * h
|
|
480
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
481
|
-
yd = plane_sdf(plane_width, plane_length, query)
|
|
482
|
-
|
|
483
|
-
if yc < yd:
|
|
484
|
-
return 0.5 * (a + d)
|
|
485
|
-
return 0.5 * (c + b)
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
@wp.func
|
|
489
|
-
def closest_edge_coordinate_capsule(radius: float, half_height: float, edge_a: wp.vec3, edge_b: wp.vec3, max_iter: int):
|
|
490
|
-
# find point on edge closest to capsule, return its barycentric edge coordinate
|
|
491
|
-
# Golden-section search
|
|
492
|
-
a = float(0.0)
|
|
493
|
-
b = float(1.0)
|
|
494
|
-
h = b - a
|
|
495
|
-
invphi = 0.61803398875 # 1 / phi
|
|
496
|
-
invphi2 = 0.38196601125 # 1 / phi^2
|
|
497
|
-
c = a + invphi2 * h
|
|
498
|
-
d = a + invphi * h
|
|
499
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
500
|
-
yc = capsule_sdf(radius, half_height, query)
|
|
501
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
502
|
-
yd = capsule_sdf(radius, half_height, query)
|
|
503
|
-
|
|
504
|
-
for _k in range(max_iter):
|
|
505
|
-
if yc < yd: # yc > yd to find the maximum
|
|
506
|
-
b = d
|
|
507
|
-
d = c
|
|
508
|
-
yd = yc
|
|
509
|
-
h = invphi * h
|
|
510
|
-
c = a + invphi2 * h
|
|
511
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
512
|
-
yc = capsule_sdf(radius, half_height, query)
|
|
513
|
-
else:
|
|
514
|
-
a = c
|
|
515
|
-
c = d
|
|
516
|
-
yc = yd
|
|
517
|
-
h = invphi * h
|
|
518
|
-
d = a + invphi * h
|
|
519
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
520
|
-
yd = capsule_sdf(radius, half_height, query)
|
|
521
|
-
|
|
522
|
-
if yc < yd:
|
|
523
|
-
return 0.5 * (a + d)
|
|
524
|
-
|
|
525
|
-
return 0.5 * (c + b)
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
@wp.func
|
|
529
|
-
def mesh_sdf(mesh: wp.uint64, point: wp.vec3, max_dist: float):
|
|
530
|
-
face_index = int(0)
|
|
531
|
-
face_u = float(0.0)
|
|
532
|
-
face_v = float(0.0)
|
|
533
|
-
sign = float(0.0)
|
|
534
|
-
res = wp.mesh_query_point_sign_normal(mesh, point, max_dist, sign, face_index, face_u, face_v)
|
|
535
|
-
|
|
536
|
-
if res:
|
|
537
|
-
closest = wp.mesh_eval_position(mesh, face_index, face_u, face_v)
|
|
538
|
-
return wp.length(point - closest) * sign
|
|
539
|
-
return max_dist
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
@wp.func
|
|
543
|
-
def closest_point_mesh(mesh: wp.uint64, point: wp.vec3, max_dist: float):
|
|
544
|
-
face_index = int(0)
|
|
545
|
-
face_u = float(0.0)
|
|
546
|
-
face_v = float(0.0)
|
|
547
|
-
sign = float(0.0)
|
|
548
|
-
res = wp.mesh_query_point_sign_normal(mesh, point, max_dist, sign, face_index, face_u, face_v)
|
|
549
|
-
|
|
550
|
-
if res:
|
|
551
|
-
return wp.mesh_eval_position(mesh, face_index, face_u, face_v)
|
|
552
|
-
# return arbitrary point from mesh
|
|
553
|
-
return wp.mesh_eval_position(mesh, 0, 0.0, 0.0)
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
@wp.func
|
|
557
|
-
def closest_edge_coordinate_mesh(mesh: wp.uint64, edge_a: wp.vec3, edge_b: wp.vec3, max_iter: int, max_dist: float):
|
|
558
|
-
# find point on edge closest to mesh, return its barycentric edge coordinate
|
|
559
|
-
# Golden-section search
|
|
560
|
-
a = float(0.0)
|
|
561
|
-
b = float(1.0)
|
|
562
|
-
h = b - a
|
|
563
|
-
invphi = 0.61803398875 # 1 / phi
|
|
564
|
-
invphi2 = 0.38196601125 # 1 / phi^2
|
|
565
|
-
c = a + invphi2 * h
|
|
566
|
-
d = a + invphi * h
|
|
567
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
568
|
-
yc = mesh_sdf(mesh, query, max_dist)
|
|
569
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
570
|
-
yd = mesh_sdf(mesh, query, max_dist)
|
|
571
|
-
|
|
572
|
-
for _k in range(max_iter):
|
|
573
|
-
if yc < yd: # yc > yd to find the maximum
|
|
574
|
-
b = d
|
|
575
|
-
d = c
|
|
576
|
-
yd = yc
|
|
577
|
-
h = invphi * h
|
|
578
|
-
c = a + invphi2 * h
|
|
579
|
-
query = (1.0 - c) * edge_a + c * edge_b
|
|
580
|
-
yc = mesh_sdf(mesh, query, max_dist)
|
|
581
|
-
else:
|
|
582
|
-
a = c
|
|
583
|
-
c = d
|
|
584
|
-
yc = yd
|
|
585
|
-
h = invphi * h
|
|
586
|
-
d = a + invphi * h
|
|
587
|
-
query = (1.0 - d) * edge_a + d * edge_b
|
|
588
|
-
yd = mesh_sdf(mesh, query, max_dist)
|
|
589
|
-
|
|
590
|
-
if yc < yd:
|
|
591
|
-
return 0.5 * (a + d)
|
|
592
|
-
return 0.5 * (c + b)
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
@wp.func
|
|
596
|
-
def volume_grad(volume: wp.uint64, p: wp.vec3):
|
|
597
|
-
eps = 0.05 # TODO make this a parameter
|
|
598
|
-
q = wp.volume_world_to_index(volume, p)
|
|
599
|
-
|
|
600
|
-
# compute gradient of the SDF using finite differences
|
|
601
|
-
dx = wp.volume_sample_f(volume, q + wp.vec3(eps, 0.0, 0.0), wp.Volume.LINEAR) - wp.volume_sample_f(
|
|
602
|
-
volume, q - wp.vec3(eps, 0.0, 0.0), wp.Volume.LINEAR
|
|
603
|
-
)
|
|
604
|
-
dy = wp.volume_sample_f(volume, q + wp.vec3(0.0, eps, 0.0), wp.Volume.LINEAR) - wp.volume_sample_f(
|
|
605
|
-
volume, q - wp.vec3(0.0, eps, 0.0), wp.Volume.LINEAR
|
|
606
|
-
)
|
|
607
|
-
dz = wp.volume_sample_f(volume, q + wp.vec3(0.0, 0.0, eps), wp.Volume.LINEAR) - wp.volume_sample_f(
|
|
608
|
-
volume, q - wp.vec3(0.0, 0.0, eps), wp.Volume.LINEAR
|
|
609
|
-
)
|
|
610
|
-
|
|
611
|
-
return wp.normalize(wp.vec3(dx, dy, dz))
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
@wp.func
|
|
615
|
-
def counter_increment(counter: wp.array(dtype=int), counter_index: int, tids: wp.array(dtype=int), tid: int):
|
|
616
|
-
# increment counter, remember which thread received which counter value
|
|
617
|
-
count = wp.atomic_add(counter, counter_index, 1)
|
|
618
|
-
tids[tid] = count
|
|
619
|
-
return count
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
@wp.func_replay(counter_increment)
|
|
623
|
-
def replay_counter_increment(counter: wp.array(dtype=int), counter_index: int, tids: wp.array(dtype=int), tid: int):
|
|
624
|
-
return tids[tid]
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
@wp.func
|
|
628
|
-
def limited_counter_increment(
|
|
629
|
-
counter: wp.array(dtype=int), counter_index: int, tids: wp.array(dtype=int), tid: int, index_limit: int
|
|
630
|
-
):
|
|
631
|
-
# increment counter but only if it is smaller than index_limit, remember which thread received which counter value
|
|
632
|
-
count = wp.atomic_add(counter, counter_index, 1)
|
|
633
|
-
if count < index_limit or index_limit < 0:
|
|
634
|
-
tids[tid] = count
|
|
635
|
-
return count
|
|
636
|
-
tids[tid] = -1
|
|
637
|
-
return -1
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
@wp.func_replay(limited_counter_increment)
|
|
641
|
-
def replay_limited_counter_increment(
|
|
642
|
-
counter: wp.array(dtype=int), counter_index: int, tids: wp.array(dtype=int), tid: int, index_limit: int
|
|
643
|
-
):
|
|
644
|
-
return tids[tid]
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
@wp.kernel
|
|
648
|
-
def create_soft_contacts(
|
|
649
|
-
particle_x: wp.array(dtype=wp.vec3),
|
|
650
|
-
particle_radius: wp.array(dtype=float),
|
|
651
|
-
particle_flags: wp.array(dtype=wp.uint32),
|
|
652
|
-
body_X_wb: wp.array(dtype=wp.transform),
|
|
653
|
-
shape_X_bs: wp.array(dtype=wp.transform),
|
|
654
|
-
shape_body: wp.array(dtype=int),
|
|
655
|
-
geo: ModelShapeGeometry,
|
|
656
|
-
margin: float,
|
|
657
|
-
soft_contact_max: int,
|
|
658
|
-
shape_count: int,
|
|
659
|
-
# outputs
|
|
660
|
-
soft_contact_count: wp.array(dtype=int),
|
|
661
|
-
soft_contact_particle: wp.array(dtype=int),
|
|
662
|
-
soft_contact_shape: wp.array(dtype=int),
|
|
663
|
-
soft_contact_body_pos: wp.array(dtype=wp.vec3),
|
|
664
|
-
soft_contact_body_vel: wp.array(dtype=wp.vec3),
|
|
665
|
-
soft_contact_normal: wp.array(dtype=wp.vec3),
|
|
666
|
-
soft_contact_tids: wp.array(dtype=int),
|
|
667
|
-
):
|
|
668
|
-
tid = wp.tid()
|
|
669
|
-
particle_index, shape_index = tid // shape_count, tid % shape_count
|
|
670
|
-
if (particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE) == 0:
|
|
671
|
-
return
|
|
672
|
-
|
|
673
|
-
rigid_index = shape_body[shape_index]
|
|
674
|
-
|
|
675
|
-
px = particle_x[particle_index]
|
|
676
|
-
radius = particle_radius[particle_index]
|
|
677
|
-
|
|
678
|
-
X_wb = wp.transform_identity()
|
|
679
|
-
if rigid_index >= 0:
|
|
680
|
-
X_wb = body_X_wb[rigid_index]
|
|
681
|
-
|
|
682
|
-
X_bs = shape_X_bs[shape_index]
|
|
683
|
-
|
|
684
|
-
X_ws = wp.transform_multiply(X_wb, X_bs)
|
|
685
|
-
X_sw = wp.transform_inverse(X_ws)
|
|
686
|
-
|
|
687
|
-
# transform particle position to shape local space
|
|
688
|
-
x_local = wp.transform_point(X_sw, px)
|
|
689
|
-
|
|
690
|
-
# geo description
|
|
691
|
-
geo_type = geo.type[shape_index]
|
|
692
|
-
geo_scale = geo.scale[shape_index]
|
|
693
|
-
|
|
694
|
-
# evaluate shape sdf
|
|
695
|
-
d = 1.0e6
|
|
696
|
-
n = wp.vec3()
|
|
697
|
-
v = wp.vec3()
|
|
698
|
-
|
|
699
|
-
if geo_type == wp.sim.GEO_SPHERE:
|
|
700
|
-
d = sphere_sdf(wp.vec3(), geo_scale[0], x_local)
|
|
701
|
-
n = sphere_sdf_grad(wp.vec3(), geo_scale[0], x_local)
|
|
702
|
-
|
|
703
|
-
if geo_type == wp.sim.GEO_BOX:
|
|
704
|
-
d = box_sdf(geo_scale, x_local)
|
|
705
|
-
n = box_sdf_grad(geo_scale, x_local)
|
|
706
|
-
|
|
707
|
-
if geo_type == wp.sim.GEO_CAPSULE:
|
|
708
|
-
d = capsule_sdf(geo_scale[0], geo_scale[1], x_local)
|
|
709
|
-
n = capsule_sdf_grad(geo_scale[0], geo_scale[1], x_local)
|
|
710
|
-
|
|
711
|
-
if geo_type == wp.sim.GEO_CYLINDER:
|
|
712
|
-
d = cylinder_sdf(geo_scale[0], geo_scale[1], x_local)
|
|
713
|
-
n = cylinder_sdf_grad(geo_scale[0], geo_scale[1], x_local)
|
|
714
|
-
|
|
715
|
-
if geo_type == wp.sim.GEO_CONE:
|
|
716
|
-
d = cone_sdf(geo_scale[0], geo_scale[1], x_local)
|
|
717
|
-
n = cone_sdf_grad(geo_scale[0], geo_scale[1], x_local)
|
|
718
|
-
|
|
719
|
-
if geo_type == wp.sim.GEO_MESH:
|
|
720
|
-
mesh = geo.source[shape_index]
|
|
721
|
-
|
|
722
|
-
face_index = int(0)
|
|
723
|
-
face_u = float(0.0)
|
|
724
|
-
face_v = float(0.0)
|
|
725
|
-
sign = float(0.0)
|
|
726
|
-
|
|
727
|
-
min_scale = wp.min(geo_scale)
|
|
728
|
-
if wp.mesh_query_point_sign_normal(
|
|
729
|
-
mesh, wp.cw_div(x_local, geo_scale), margin + radius / min_scale, sign, face_index, face_u, face_v
|
|
730
|
-
):
|
|
731
|
-
shape_p = wp.mesh_eval_position(mesh, face_index, face_u, face_v)
|
|
732
|
-
shape_v = wp.mesh_eval_velocity(mesh, face_index, face_u, face_v)
|
|
733
|
-
|
|
734
|
-
shape_p = wp.cw_mul(shape_p, geo_scale)
|
|
735
|
-
shape_v = wp.cw_mul(shape_v, geo_scale)
|
|
736
|
-
|
|
737
|
-
delta = x_local - shape_p
|
|
738
|
-
|
|
739
|
-
d = wp.length(delta) * sign
|
|
740
|
-
n = wp.normalize(delta) * sign
|
|
741
|
-
v = shape_v
|
|
742
|
-
|
|
743
|
-
if geo_type == wp.sim.GEO_SDF:
|
|
744
|
-
volume = geo.source[shape_index]
|
|
745
|
-
xpred_local = wp.volume_world_to_index(volume, wp.cw_div(x_local, geo_scale))
|
|
746
|
-
nn = wp.vec3(0.0, 0.0, 0.0)
|
|
747
|
-
d = wp.volume_sample_grad_f(volume, xpred_local, wp.Volume.LINEAR, nn)
|
|
748
|
-
n = wp.normalize(nn)
|
|
749
|
-
|
|
750
|
-
if geo_type == wp.sim.GEO_PLANE:
|
|
751
|
-
d = plane_sdf(geo_scale[0], geo_scale[1], x_local)
|
|
752
|
-
n = wp.vec3(0.0, 1.0, 0.0)
|
|
753
|
-
|
|
754
|
-
if d < margin + radius:
|
|
755
|
-
index = counter_increment(soft_contact_count, 0, soft_contact_tids, tid)
|
|
756
|
-
|
|
757
|
-
if index < soft_contact_max:
|
|
758
|
-
# compute contact point in body local space
|
|
759
|
-
body_pos = wp.transform_point(X_bs, x_local - n * d)
|
|
760
|
-
body_vel = wp.transform_vector(X_bs, v)
|
|
761
|
-
|
|
762
|
-
world_normal = wp.transform_vector(X_ws, n)
|
|
763
|
-
|
|
764
|
-
soft_contact_shape[index] = shape_index
|
|
765
|
-
soft_contact_body_pos[index] = body_pos
|
|
766
|
-
soft_contact_body_vel[index] = body_vel
|
|
767
|
-
soft_contact_particle[index] = particle_index
|
|
768
|
-
soft_contact_normal[index] = world_normal
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
@wp.kernel(enable_backward=False)
|
|
772
|
-
def count_contact_points(
|
|
773
|
-
contact_pairs: wp.array(dtype=int, ndim=2),
|
|
774
|
-
geo: ModelShapeGeometry,
|
|
775
|
-
mesh_contact_max: int,
|
|
776
|
-
# outputs
|
|
777
|
-
contact_count: wp.array(dtype=int),
|
|
778
|
-
):
|
|
779
|
-
tid = wp.tid()
|
|
780
|
-
shape_a = contact_pairs[tid, 0]
|
|
781
|
-
shape_b = contact_pairs[tid, 1]
|
|
782
|
-
|
|
783
|
-
if shape_b == -1:
|
|
784
|
-
actual_shape_a = shape_a
|
|
785
|
-
actual_type_a = geo.type[shape_a]
|
|
786
|
-
# ground plane
|
|
787
|
-
actual_type_b = wp.sim.GEO_PLANE
|
|
788
|
-
actual_shape_b = -1
|
|
789
|
-
else:
|
|
790
|
-
type_a = geo.type[shape_a]
|
|
791
|
-
type_b = geo.type[shape_b]
|
|
792
|
-
# unique ordering of shape pairs
|
|
793
|
-
if type_a < type_b:
|
|
794
|
-
actual_shape_a = shape_a
|
|
795
|
-
actual_shape_b = shape_b
|
|
796
|
-
actual_type_a = type_a
|
|
797
|
-
actual_type_b = type_b
|
|
798
|
-
else:
|
|
799
|
-
actual_shape_a = shape_b
|
|
800
|
-
actual_shape_b = shape_a
|
|
801
|
-
actual_type_a = type_b
|
|
802
|
-
actual_type_b = type_a
|
|
803
|
-
|
|
804
|
-
# determine how many contact points need to be evaluated
|
|
805
|
-
num_contacts = 0
|
|
806
|
-
num_actual_contacts = 0
|
|
807
|
-
if actual_type_a == wp.sim.GEO_SPHERE:
|
|
808
|
-
num_contacts = 1
|
|
809
|
-
num_actual_contacts = 1
|
|
810
|
-
elif actual_type_a == wp.sim.GEO_CAPSULE:
|
|
811
|
-
if actual_type_b == wp.sim.GEO_PLANE:
|
|
812
|
-
if geo.scale[actual_shape_b][0] == 0.0 and geo.scale[actual_shape_b][1] == 0.0:
|
|
813
|
-
num_contacts = 2 # vertex-based collision for infinite plane
|
|
814
|
-
num_actual_contacts = 2
|
|
815
|
-
else:
|
|
816
|
-
num_contacts = 2 + 4 # vertex-based collision + plane edges
|
|
817
|
-
num_actual_contacts = 2 + 4
|
|
818
|
-
elif actual_type_b == wp.sim.GEO_MESH:
|
|
819
|
-
num_contacts_a = 2
|
|
820
|
-
mesh_b = wp.mesh_get(geo.source[actual_shape_b])
|
|
821
|
-
num_contacts_b = mesh_b.points.shape[0]
|
|
822
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
823
|
-
if mesh_contact_max > 0:
|
|
824
|
-
num_contacts_b = wp.min(mesh_contact_max, num_contacts_b)
|
|
825
|
-
num_actual_contacts = num_contacts_a + num_contacts_b
|
|
826
|
-
else:
|
|
827
|
-
num_contacts = 2
|
|
828
|
-
num_actual_contacts = 2
|
|
829
|
-
elif actual_type_a == wp.sim.GEO_BOX:
|
|
830
|
-
if actual_type_b == wp.sim.GEO_BOX:
|
|
831
|
-
num_contacts = 24
|
|
832
|
-
num_actual_contacts = 24
|
|
833
|
-
elif actual_type_b == wp.sim.GEO_MESH:
|
|
834
|
-
num_contacts_a = 8
|
|
835
|
-
mesh_b = wp.mesh_get(geo.source[actual_shape_b])
|
|
836
|
-
num_contacts_b = mesh_b.points.shape[0]
|
|
837
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
838
|
-
if mesh_contact_max > 0:
|
|
839
|
-
num_contacts_b = wp.min(mesh_contact_max, num_contacts_b)
|
|
840
|
-
num_actual_contacts = num_contacts_a + num_contacts_b
|
|
841
|
-
elif actual_type_b == wp.sim.GEO_PLANE:
|
|
842
|
-
if geo.scale[actual_shape_b][0] == 0.0 and geo.scale[actual_shape_b][1] == 0.0:
|
|
843
|
-
num_contacts = 8 # vertex-based collision
|
|
844
|
-
num_actual_contacts = 8
|
|
845
|
-
else:
|
|
846
|
-
num_contacts = 8 + 4 # vertex-based collision + plane edges
|
|
847
|
-
num_actual_contacts = 8 + 4
|
|
848
|
-
else:
|
|
849
|
-
num_contacts = 8
|
|
850
|
-
num_actual_contacts = 8
|
|
851
|
-
elif actual_type_a == wp.sim.GEO_MESH:
|
|
852
|
-
mesh_a = wp.mesh_get(geo.source[actual_shape_a])
|
|
853
|
-
num_contacts_a = mesh_a.points.shape[0]
|
|
854
|
-
if mesh_contact_max > 0:
|
|
855
|
-
num_contacts_a = wp.min(mesh_contact_max, num_contacts_a)
|
|
856
|
-
if actual_type_b == wp.sim.GEO_MESH:
|
|
857
|
-
mesh_b = wp.mesh_get(geo.source[actual_shape_b])
|
|
858
|
-
num_contacts_b = mesh_b.points.shape[0]
|
|
859
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
860
|
-
if mesh_contact_max > 0:
|
|
861
|
-
num_contacts_b = wp.min(mesh_contact_max, num_contacts_b)
|
|
862
|
-
else:
|
|
863
|
-
num_contacts_b = 0
|
|
864
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
865
|
-
num_actual_contacts = num_contacts_a + num_contacts_b
|
|
866
|
-
elif actual_type_a == wp.sim.GEO_PLANE:
|
|
867
|
-
return # no plane-plane contacts
|
|
868
|
-
else:
|
|
869
|
-
wp.printf(
|
|
870
|
-
"count_contact_points: unsupported geometry type combination %d and %d\n", actual_type_a, actual_type_b
|
|
871
|
-
)
|
|
872
|
-
|
|
873
|
-
wp.atomic_add(contact_count, 0, num_contacts)
|
|
874
|
-
wp.atomic_add(contact_count, 1, num_actual_contacts)
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
@wp.kernel(enable_backward=False)
|
|
878
|
-
def broadphase_collision_pairs(
|
|
879
|
-
contact_pairs: wp.array(dtype=int, ndim=2),
|
|
880
|
-
body_q: wp.array(dtype=wp.transform),
|
|
881
|
-
shape_X_bs: wp.array(dtype=wp.transform),
|
|
882
|
-
shape_body: wp.array(dtype=int),
|
|
883
|
-
body_mass: wp.array(dtype=float),
|
|
884
|
-
num_shapes: int,
|
|
885
|
-
geo: ModelShapeGeometry,
|
|
886
|
-
collision_radius: wp.array(dtype=float),
|
|
887
|
-
rigid_contact_max: int,
|
|
888
|
-
rigid_contact_margin: float,
|
|
889
|
-
mesh_contact_max: int,
|
|
890
|
-
iterate_mesh_vertices: bool,
|
|
891
|
-
# outputs
|
|
892
|
-
contact_count: wp.array(dtype=int),
|
|
893
|
-
contact_shape0: wp.array(dtype=int),
|
|
894
|
-
contact_shape1: wp.array(dtype=int),
|
|
895
|
-
contact_point_id: wp.array(dtype=int),
|
|
896
|
-
contact_point_limit: wp.array(dtype=int),
|
|
897
|
-
):
|
|
898
|
-
tid = wp.tid()
|
|
899
|
-
shape_a = contact_pairs[tid, 0]
|
|
900
|
-
shape_b = contact_pairs[tid, 1]
|
|
901
|
-
|
|
902
|
-
mass_a = 0.0
|
|
903
|
-
mass_b = 0.0
|
|
904
|
-
rigid_a = shape_body[shape_a]
|
|
905
|
-
if rigid_a == -1:
|
|
906
|
-
X_ws_a = shape_X_bs[shape_a]
|
|
907
|
-
else:
|
|
908
|
-
X_ws_a = wp.transform_multiply(body_q[rigid_a], shape_X_bs[shape_a])
|
|
909
|
-
mass_a = body_mass[rigid_a]
|
|
910
|
-
rigid_b = shape_body[shape_b]
|
|
911
|
-
if rigid_b == -1:
|
|
912
|
-
X_ws_b = shape_X_bs[shape_b]
|
|
913
|
-
else:
|
|
914
|
-
X_ws_b = wp.transform_multiply(body_q[rigid_b], shape_X_bs[shape_b])
|
|
915
|
-
mass_b = body_mass[rigid_b]
|
|
916
|
-
if mass_a == 0.0 and mass_b == 0.0:
|
|
917
|
-
# skip if both bodies are static
|
|
918
|
-
return
|
|
919
|
-
|
|
920
|
-
type_a = geo.type[shape_a]
|
|
921
|
-
type_b = geo.type[shape_b]
|
|
922
|
-
# unique ordering of shape pairs
|
|
923
|
-
if type_a < type_b:
|
|
924
|
-
actual_shape_a = shape_a
|
|
925
|
-
actual_shape_b = shape_b
|
|
926
|
-
actual_type_a = type_a
|
|
927
|
-
actual_type_b = type_b
|
|
928
|
-
actual_X_ws_a = X_ws_a
|
|
929
|
-
actual_X_ws_b = X_ws_b
|
|
930
|
-
else:
|
|
931
|
-
actual_shape_a = shape_b
|
|
932
|
-
actual_shape_b = shape_a
|
|
933
|
-
actual_type_a = type_b
|
|
934
|
-
actual_type_b = type_a
|
|
935
|
-
actual_X_ws_a = X_ws_b
|
|
936
|
-
actual_X_ws_b = X_ws_a
|
|
937
|
-
|
|
938
|
-
p_a = wp.transform_get_translation(actual_X_ws_a)
|
|
939
|
-
if actual_type_b == wp.sim.GEO_PLANE:
|
|
940
|
-
if actual_type_a == wp.sim.GEO_PLANE:
|
|
941
|
-
return
|
|
942
|
-
query_b = wp.transform_point(wp.transform_inverse(actual_X_ws_b), p_a)
|
|
943
|
-
scale = geo.scale[actual_shape_b]
|
|
944
|
-
closest = closest_point_plane(scale[0], scale[1], query_b)
|
|
945
|
-
d = wp.length(query_b - closest)
|
|
946
|
-
r_a = collision_radius[actual_shape_a]
|
|
947
|
-
if d > r_a + rigid_contact_margin:
|
|
948
|
-
return
|
|
949
|
-
else:
|
|
950
|
-
p_b = wp.transform_get_translation(actual_X_ws_b)
|
|
951
|
-
d = wp.length(p_a - p_b) * 0.5 - 0.1
|
|
952
|
-
r_a = collision_radius[actual_shape_a]
|
|
953
|
-
r_b = collision_radius[actual_shape_b]
|
|
954
|
-
if d > r_a + r_b + rigid_contact_margin:
|
|
955
|
-
return
|
|
956
|
-
|
|
957
|
-
pair_index_ab = actual_shape_a * num_shapes + actual_shape_b
|
|
958
|
-
pair_index_ba = actual_shape_b * num_shapes + actual_shape_a
|
|
959
|
-
|
|
960
|
-
# determine how many contact points need to be evaluated
|
|
961
|
-
num_contacts = 0
|
|
962
|
-
if actual_type_a == wp.sim.GEO_SPHERE:
|
|
963
|
-
num_contacts = 1
|
|
964
|
-
elif actual_type_a == wp.sim.GEO_CAPSULE:
|
|
965
|
-
if actual_type_b == wp.sim.GEO_PLANE:
|
|
966
|
-
if geo.scale[actual_shape_b][0] == 0.0 and geo.scale[actual_shape_b][1] == 0.0:
|
|
967
|
-
num_contacts = 2 # vertex-based collision for infinite plane
|
|
968
|
-
else:
|
|
969
|
-
num_contacts = 2 + 4 # vertex-based collision + plane edges
|
|
970
|
-
elif actual_type_b == wp.sim.GEO_MESH:
|
|
971
|
-
num_contacts_a = 2
|
|
972
|
-
mesh_b = wp.mesh_get(geo.source[actual_shape_b])
|
|
973
|
-
if iterate_mesh_vertices:
|
|
974
|
-
num_contacts_b = mesh_b.points.shape[0]
|
|
975
|
-
else:
|
|
976
|
-
num_contacts_b = 0
|
|
977
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
978
|
-
index = wp.atomic_add(contact_count, 0, num_contacts)
|
|
979
|
-
if index + num_contacts - 1 >= rigid_contact_max:
|
|
980
|
-
print("Number of rigid contacts exceeded limit. Increase Model.rigid_contact_max.")
|
|
981
|
-
return
|
|
982
|
-
# allocate contact points from capsule A against mesh B
|
|
983
|
-
for i in range(num_contacts_a):
|
|
984
|
-
contact_shape0[index + i] = actual_shape_a
|
|
985
|
-
contact_shape1[index + i] = actual_shape_b
|
|
986
|
-
contact_point_id[index + i] = i
|
|
987
|
-
# allocate contact points from mesh B against capsule A
|
|
988
|
-
for i in range(num_contacts_b):
|
|
989
|
-
contact_shape0[index + num_contacts_a + i] = actual_shape_b
|
|
990
|
-
contact_shape1[index + num_contacts_a + i] = actual_shape_a
|
|
991
|
-
contact_point_id[index + num_contacts_a + i] = i
|
|
992
|
-
if mesh_contact_max > 0 and contact_point_limit and pair_index_ba < contact_point_limit.shape[0]:
|
|
993
|
-
num_contacts_b = wp.min(mesh_contact_max, num_contacts_b)
|
|
994
|
-
contact_point_limit[pair_index_ba] = num_contacts_b
|
|
995
|
-
return
|
|
996
|
-
else:
|
|
997
|
-
num_contacts = 2
|
|
998
|
-
elif actual_type_a == wp.sim.GEO_BOX:
|
|
999
|
-
if actual_type_b == wp.sim.GEO_BOX:
|
|
1000
|
-
index = wp.atomic_add(contact_count, 0, 24)
|
|
1001
|
-
if index + 23 >= rigid_contact_max:
|
|
1002
|
-
print("Number of rigid contacts exceeded limit. Increase Model.rigid_contact_max.")
|
|
1003
|
-
return
|
|
1004
|
-
# allocate contact points from box A against B
|
|
1005
|
-
for i in range(12): # 12 edges
|
|
1006
|
-
contact_shape0[index + i] = shape_a
|
|
1007
|
-
contact_shape1[index + i] = shape_b
|
|
1008
|
-
contact_point_id[index + i] = i
|
|
1009
|
-
# allocate contact points from box B against A
|
|
1010
|
-
for i in range(12):
|
|
1011
|
-
contact_shape0[index + 12 + i] = shape_b
|
|
1012
|
-
contact_shape1[index + 12 + i] = shape_a
|
|
1013
|
-
contact_point_id[index + 12 + i] = i
|
|
1014
|
-
return
|
|
1015
|
-
elif actual_type_b == wp.sim.GEO_MESH:
|
|
1016
|
-
num_contacts_a = 8
|
|
1017
|
-
mesh_b = wp.mesh_get(geo.source[actual_shape_b])
|
|
1018
|
-
if iterate_mesh_vertices:
|
|
1019
|
-
num_contacts_b = mesh_b.points.shape[0]
|
|
1020
|
-
else:
|
|
1021
|
-
num_contacts_b = 0
|
|
1022
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
1023
|
-
index = wp.atomic_add(contact_count, 0, num_contacts)
|
|
1024
|
-
if index + num_contacts - 1 >= rigid_contact_max:
|
|
1025
|
-
print("Number of rigid contacts exceeded limit. Increase Model.rigid_contact_max.")
|
|
1026
|
-
return
|
|
1027
|
-
# allocate contact points from box A against mesh B
|
|
1028
|
-
for i in range(num_contacts_a):
|
|
1029
|
-
contact_shape0[index + i] = actual_shape_a
|
|
1030
|
-
contact_shape1[index + i] = actual_shape_b
|
|
1031
|
-
contact_point_id[index + i] = i
|
|
1032
|
-
# allocate contact points from mesh B against box A
|
|
1033
|
-
for i in range(num_contacts_b):
|
|
1034
|
-
contact_shape0[index + num_contacts_a + i] = actual_shape_b
|
|
1035
|
-
contact_shape1[index + num_contacts_a + i] = actual_shape_a
|
|
1036
|
-
contact_point_id[index + num_contacts_a + i] = i
|
|
1037
|
-
|
|
1038
|
-
if mesh_contact_max > 0 and contact_point_limit and pair_index_ba < contact_point_limit.shape[0]:
|
|
1039
|
-
num_contacts_b = wp.min(mesh_contact_max, num_contacts_b)
|
|
1040
|
-
contact_point_limit[pair_index_ba] = num_contacts_b
|
|
1041
|
-
return
|
|
1042
|
-
elif actual_type_b == wp.sim.GEO_PLANE:
|
|
1043
|
-
if geo.scale[actual_shape_b][0] == 0.0 and geo.scale[actual_shape_b][1] == 0.0:
|
|
1044
|
-
num_contacts = 8 # vertex-based collision
|
|
1045
|
-
else:
|
|
1046
|
-
num_contacts = 8 + 4 # vertex-based collision + plane edges
|
|
1047
|
-
else:
|
|
1048
|
-
num_contacts = 8
|
|
1049
|
-
elif actual_type_a == wp.sim.GEO_MESH:
|
|
1050
|
-
mesh_a = wp.mesh_get(geo.source[actual_shape_a])
|
|
1051
|
-
num_contacts_a = mesh_a.points.shape[0]
|
|
1052
|
-
num_contacts_b = 0
|
|
1053
|
-
if actual_type_b == wp.sim.GEO_MESH:
|
|
1054
|
-
mesh_b = wp.mesh_get(geo.source[actual_shape_b])
|
|
1055
|
-
num_contacts_b = mesh_b.points.shape[0]
|
|
1056
|
-
elif actual_type_b != wp.sim.GEO_PLANE:
|
|
1057
|
-
print("broadphase_collision_pairs: unsupported geometry type for mesh collision")
|
|
1058
|
-
return
|
|
1059
|
-
num_contacts = num_contacts_a + num_contacts_b
|
|
1060
|
-
if num_contacts > 0:
|
|
1061
|
-
index = wp.atomic_add(contact_count, 0, num_contacts)
|
|
1062
|
-
if index + num_contacts - 1 >= rigid_contact_max:
|
|
1063
|
-
print("Mesh contact: Number of rigid contacts exceeded limit. Increase Model.rigid_contact_max.")
|
|
1064
|
-
return
|
|
1065
|
-
# allocate contact points from mesh A against B
|
|
1066
|
-
for i in range(num_contacts_a):
|
|
1067
|
-
contact_shape0[index + i] = actual_shape_a
|
|
1068
|
-
contact_shape1[index + i] = actual_shape_b
|
|
1069
|
-
contact_point_id[index + i] = i
|
|
1070
|
-
# allocate contact points from mesh B against A
|
|
1071
|
-
for i in range(num_contacts_b):
|
|
1072
|
-
contact_shape0[index + num_contacts_a + i] = actual_shape_b
|
|
1073
|
-
contact_shape1[index + num_contacts_a + i] = actual_shape_a
|
|
1074
|
-
contact_point_id[index + num_contacts_a + i] = i
|
|
1075
|
-
|
|
1076
|
-
if mesh_contact_max > 0 and contact_point_limit:
|
|
1077
|
-
num_contacts_a = wp.min(mesh_contact_max, num_contacts_a)
|
|
1078
|
-
num_contacts_b = wp.min(mesh_contact_max, num_contacts_b)
|
|
1079
|
-
if pair_index_ab < contact_point_limit.shape[0]:
|
|
1080
|
-
contact_point_limit[pair_index_ab] = num_contacts_a
|
|
1081
|
-
if pair_index_ba < contact_point_limit.shape[0]:
|
|
1082
|
-
contact_point_limit[pair_index_ba] = num_contacts_b
|
|
1083
|
-
return
|
|
1084
|
-
elif actual_type_a == wp.sim.GEO_PLANE:
|
|
1085
|
-
return # no plane-plane contacts
|
|
1086
|
-
else:
|
|
1087
|
-
print("broadphase_collision_pairs: unsupported geometry type")
|
|
1088
|
-
|
|
1089
|
-
if num_contacts > 0:
|
|
1090
|
-
index = wp.atomic_add(contact_count, 0, num_contacts)
|
|
1091
|
-
if index + num_contacts - 1 >= rigid_contact_max:
|
|
1092
|
-
print("Number of rigid contacts exceeded limit. Increase Model.rigid_contact_max.")
|
|
1093
|
-
return
|
|
1094
|
-
# allocate contact points
|
|
1095
|
-
for i in range(num_contacts):
|
|
1096
|
-
cp_index = index + i
|
|
1097
|
-
contact_shape0[cp_index] = actual_shape_a
|
|
1098
|
-
contact_shape1[cp_index] = actual_shape_b
|
|
1099
|
-
contact_point_id[cp_index] = i
|
|
1100
|
-
if contact_point_limit:
|
|
1101
|
-
if pair_index_ab < contact_point_limit.shape[0]:
|
|
1102
|
-
contact_point_limit[pair_index_ab] = num_contacts
|
|
1103
|
-
if pair_index_ba < contact_point_limit.shape[0]:
|
|
1104
|
-
contact_point_limit[pair_index_ba] = 0
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
@wp.kernel
|
|
1108
|
-
def handle_contact_pairs(
|
|
1109
|
-
body_q: wp.array(dtype=wp.transform),
|
|
1110
|
-
shape_X_bs: wp.array(dtype=wp.transform),
|
|
1111
|
-
shape_body: wp.array(dtype=int),
|
|
1112
|
-
geo: ModelShapeGeometry,
|
|
1113
|
-
rigid_contact_margin: float,
|
|
1114
|
-
contact_broad_shape0: wp.array(dtype=int),
|
|
1115
|
-
contact_broad_shape1: wp.array(dtype=int),
|
|
1116
|
-
num_shapes: int,
|
|
1117
|
-
contact_point_id: wp.array(dtype=int),
|
|
1118
|
-
contact_point_limit: wp.array(dtype=int),
|
|
1119
|
-
edge_sdf_iter: int,
|
|
1120
|
-
# outputs
|
|
1121
|
-
contact_count: wp.array(dtype=int),
|
|
1122
|
-
contact_shape0: wp.array(dtype=int),
|
|
1123
|
-
contact_shape1: wp.array(dtype=int),
|
|
1124
|
-
contact_point0: wp.array(dtype=wp.vec3),
|
|
1125
|
-
contact_point1: wp.array(dtype=wp.vec3),
|
|
1126
|
-
contact_offset0: wp.array(dtype=wp.vec3),
|
|
1127
|
-
contact_offset1: wp.array(dtype=wp.vec3),
|
|
1128
|
-
contact_normal: wp.array(dtype=wp.vec3),
|
|
1129
|
-
contact_thickness: wp.array(dtype=float),
|
|
1130
|
-
contact_pairwise_counter: wp.array(dtype=int),
|
|
1131
|
-
contact_tids: wp.array(dtype=int),
|
|
1132
|
-
):
|
|
1133
|
-
tid = wp.tid()
|
|
1134
|
-
shape_a = contact_broad_shape0[tid]
|
|
1135
|
-
shape_b = contact_broad_shape1[tid]
|
|
1136
|
-
if shape_a == shape_b:
|
|
1137
|
-
return
|
|
1138
|
-
|
|
1139
|
-
if contact_point_limit:
|
|
1140
|
-
pair_index = shape_a * num_shapes + shape_b
|
|
1141
|
-
contact_limit = contact_point_limit[pair_index]
|
|
1142
|
-
if contact_pairwise_counter[pair_index] >= contact_limit:
|
|
1143
|
-
# reached limit of contact points per contact pair
|
|
1144
|
-
return
|
|
1145
|
-
|
|
1146
|
-
point_id = contact_point_id[tid]
|
|
1147
|
-
|
|
1148
|
-
rigid_a = shape_body[shape_a]
|
|
1149
|
-
X_wb_a = wp.transform_identity()
|
|
1150
|
-
if rigid_a >= 0:
|
|
1151
|
-
X_wb_a = body_q[rigid_a]
|
|
1152
|
-
X_bs_a = shape_X_bs[shape_a]
|
|
1153
|
-
X_ws_a = wp.transform_multiply(X_wb_a, X_bs_a)
|
|
1154
|
-
X_sw_a = wp.transform_inverse(X_ws_a)
|
|
1155
|
-
X_bw_a = wp.transform_inverse(X_wb_a)
|
|
1156
|
-
geo_type_a = geo.type[shape_a]
|
|
1157
|
-
geo_scale_a = geo.scale[shape_a]
|
|
1158
|
-
min_scale_a = min(geo_scale_a)
|
|
1159
|
-
thickness_a = geo.thickness[shape_a]
|
|
1160
|
-
# is_solid_a = geo.is_solid[shape_a]
|
|
1161
|
-
|
|
1162
|
-
rigid_b = shape_body[shape_b]
|
|
1163
|
-
X_wb_b = wp.transform_identity()
|
|
1164
|
-
if rigid_b >= 0:
|
|
1165
|
-
X_wb_b = body_q[rigid_b]
|
|
1166
|
-
X_bs_b = shape_X_bs[shape_b]
|
|
1167
|
-
X_ws_b = wp.transform_multiply(X_wb_b, X_bs_b)
|
|
1168
|
-
X_sw_b = wp.transform_inverse(X_ws_b)
|
|
1169
|
-
X_bw_b = wp.transform_inverse(X_wb_b)
|
|
1170
|
-
geo_type_b = geo.type[shape_b]
|
|
1171
|
-
geo_scale_b = geo.scale[shape_b]
|
|
1172
|
-
min_scale_b = min(geo_scale_b)
|
|
1173
|
-
thickness_b = geo.thickness[shape_b]
|
|
1174
|
-
# is_solid_b = geo.is_solid[shape_b]
|
|
1175
|
-
|
|
1176
|
-
distance = 1.0e6
|
|
1177
|
-
u = float(0.0)
|
|
1178
|
-
thickness = thickness_a + thickness_b
|
|
1179
|
-
|
|
1180
|
-
if geo_type_a == wp.sim.GEO_SPHERE:
|
|
1181
|
-
p_a_world = wp.transform_get_translation(X_ws_a)
|
|
1182
|
-
if geo_type_b == wp.sim.GEO_SPHERE:
|
|
1183
|
-
p_b_world = wp.transform_get_translation(X_ws_b)
|
|
1184
|
-
elif geo_type_b == wp.sim.GEO_BOX:
|
|
1185
|
-
# contact point in frame of body B
|
|
1186
|
-
p_a_body = wp.transform_point(X_sw_b, p_a_world)
|
|
1187
|
-
p_b_body = closest_point_box(geo_scale_b, p_a_body)
|
|
1188
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1189
|
-
elif geo_type_b == wp.sim.GEO_CAPSULE:
|
|
1190
|
-
half_height_b = geo_scale_b[1]
|
|
1191
|
-
# capsule B
|
|
1192
|
-
A_b = wp.transform_point(X_ws_b, wp.vec3(0.0, half_height_b, 0.0))
|
|
1193
|
-
B_b = wp.transform_point(X_ws_b, wp.vec3(0.0, -half_height_b, 0.0))
|
|
1194
|
-
p_b_world = closest_point_line_segment(A_b, B_b, p_a_world)
|
|
1195
|
-
elif geo_type_b == wp.sim.GEO_MESH:
|
|
1196
|
-
mesh_b = geo.source[shape_b]
|
|
1197
|
-
query_b_local = wp.transform_point(X_sw_b, p_a_world)
|
|
1198
|
-
face_index = int(0)
|
|
1199
|
-
face_u = float(0.0)
|
|
1200
|
-
face_v = float(0.0)
|
|
1201
|
-
sign = float(0.0)
|
|
1202
|
-
max_dist = (thickness + rigid_contact_margin) / min_scale_b
|
|
1203
|
-
res = wp.mesh_query_point_sign_normal(
|
|
1204
|
-
mesh_b, wp.cw_div(query_b_local, geo_scale_b), max_dist, sign, face_index, face_u, face_v
|
|
1205
|
-
)
|
|
1206
|
-
if res:
|
|
1207
|
-
shape_p = wp.mesh_eval_position(mesh_b, face_index, face_u, face_v)
|
|
1208
|
-
shape_p = wp.cw_mul(shape_p, geo_scale_b)
|
|
1209
|
-
p_b_world = wp.transform_point(X_ws_b, shape_p)
|
|
1210
|
-
else:
|
|
1211
|
-
return
|
|
1212
|
-
elif geo_type_b == wp.sim.GEO_PLANE:
|
|
1213
|
-
p_b_body = closest_point_plane(geo_scale_b[0], geo_scale_b[1], wp.transform_point(X_sw_b, p_a_world))
|
|
1214
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1215
|
-
else:
|
|
1216
|
-
print("Unsupported geometry type in sphere collision handling")
|
|
1217
|
-
print(geo_type_b)
|
|
1218
|
-
return
|
|
1219
|
-
diff = p_a_world - p_b_world
|
|
1220
|
-
normal = wp.normalize(diff)
|
|
1221
|
-
distance = wp.dot(diff, normal)
|
|
1222
|
-
|
|
1223
|
-
elif geo_type_a == wp.sim.GEO_BOX and geo_type_b == wp.sim.GEO_BOX:
|
|
1224
|
-
# edge-based box contact
|
|
1225
|
-
edge = get_box_edge(point_id, geo_scale_a)
|
|
1226
|
-
edge0_world = wp.transform_point(X_ws_a, wp.spatial_top(edge))
|
|
1227
|
-
edge1_world = wp.transform_point(X_ws_a, wp.spatial_bottom(edge))
|
|
1228
|
-
edge0_b = wp.transform_point(X_sw_b, edge0_world)
|
|
1229
|
-
edge1_b = wp.transform_point(X_sw_b, edge1_world)
|
|
1230
|
-
max_iter = edge_sdf_iter
|
|
1231
|
-
u = closest_edge_coordinate_box(geo_scale_b, edge0_b, edge1_b, max_iter)
|
|
1232
|
-
p_a_world = (1.0 - u) * edge0_world + u * edge1_world
|
|
1233
|
-
|
|
1234
|
-
# find closest point + contact normal on box B
|
|
1235
|
-
query_b = wp.transform_point(X_sw_b, p_a_world)
|
|
1236
|
-
p_b_body = closest_point_box(geo_scale_b, query_b)
|
|
1237
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1238
|
-
diff = p_a_world - p_b_world
|
|
1239
|
-
|
|
1240
|
-
normal = wp.transform_vector(X_ws_b, box_sdf_grad(geo_scale_b, query_b))
|
|
1241
|
-
distance = wp.dot(diff, normal)
|
|
1242
|
-
|
|
1243
|
-
elif geo_type_a == wp.sim.GEO_BOX and geo_type_b == wp.sim.GEO_CAPSULE:
|
|
1244
|
-
half_height_b = geo_scale_b[1]
|
|
1245
|
-
# capsule B
|
|
1246
|
-
# depending on point id, we query an edge from 0 to 0.5 or 0.5 to 1
|
|
1247
|
-
e0 = wp.vec3(0.0, -half_height_b * float(point_id % 2), 0.0)
|
|
1248
|
-
e1 = wp.vec3(0.0, half_height_b * float((point_id + 1) % 2), 0.0)
|
|
1249
|
-
edge0_world = wp.transform_point(X_ws_b, e0)
|
|
1250
|
-
edge1_world = wp.transform_point(X_ws_b, e1)
|
|
1251
|
-
edge0_a = wp.transform_point(X_sw_a, edge0_world)
|
|
1252
|
-
edge1_a = wp.transform_point(X_sw_a, edge1_world)
|
|
1253
|
-
max_iter = edge_sdf_iter
|
|
1254
|
-
u = closest_edge_coordinate_box(geo_scale_a, edge0_a, edge1_a, max_iter)
|
|
1255
|
-
p_b_world = (1.0 - u) * edge0_world + u * edge1_world
|
|
1256
|
-
# find closest point + contact normal on box A
|
|
1257
|
-
query_a = wp.transform_point(X_sw_a, p_b_world)
|
|
1258
|
-
p_a_body = closest_point_box(geo_scale_a, query_a)
|
|
1259
|
-
p_a_world = wp.transform_point(X_ws_a, p_a_body)
|
|
1260
|
-
diff = p_a_world - p_b_world
|
|
1261
|
-
# the contact point inside the capsule should already be outside the box
|
|
1262
|
-
normal = -wp.transform_vector(X_ws_a, box_sdf_grad(geo_scale_a, query_a))
|
|
1263
|
-
distance = wp.dot(diff, normal)
|
|
1264
|
-
|
|
1265
|
-
elif geo_type_a == wp.sim.GEO_BOX and geo_type_b == wp.sim.GEO_PLANE:
|
|
1266
|
-
plane_width = geo_scale_b[0]
|
|
1267
|
-
plane_length = geo_scale_b[1]
|
|
1268
|
-
if point_id < 8:
|
|
1269
|
-
# vertex-based contact
|
|
1270
|
-
p_a_body = get_box_vertex(point_id, geo_scale_a)
|
|
1271
|
-
p_a_world = wp.transform_point(X_ws_a, p_a_body)
|
|
1272
|
-
query_b = wp.transform_point(X_sw_b, p_a_world)
|
|
1273
|
-
p_b_body = closest_point_plane(plane_width, plane_length, query_b)
|
|
1274
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1275
|
-
diff = p_a_world - p_b_world
|
|
1276
|
-
normal = wp.transform_vector(X_ws_b, wp.vec3(0.0, 1.0, 0.0))
|
|
1277
|
-
if plane_width > 0.0 and plane_length > 0.0:
|
|
1278
|
-
if wp.abs(query_b[0]) > plane_width or wp.abs(query_b[2]) > plane_length:
|
|
1279
|
-
# skip, we will evaluate the plane edge contact with the box later
|
|
1280
|
-
return
|
|
1281
|
-
# check whether the COM is above the plane
|
|
1282
|
-
# sign = wp.sign(wp.dot(wp.transform_get_translation(X_ws_a) - p_b_world, normal))
|
|
1283
|
-
# if sign < 0.0:
|
|
1284
|
-
# # the entire box is most likely below the plane
|
|
1285
|
-
# return
|
|
1286
|
-
# the contact point is within plane boundaries
|
|
1287
|
-
distance = wp.dot(diff, normal)
|
|
1288
|
-
else:
|
|
1289
|
-
# contact between box A and edges of finite plane B
|
|
1290
|
-
edge = get_plane_edge(point_id - 8, plane_width, plane_length)
|
|
1291
|
-
edge0_world = wp.transform_point(X_ws_b, wp.spatial_top(edge))
|
|
1292
|
-
edge1_world = wp.transform_point(X_ws_b, wp.spatial_bottom(edge))
|
|
1293
|
-
edge0_a = wp.transform_point(X_sw_a, edge0_world)
|
|
1294
|
-
edge1_a = wp.transform_point(X_sw_a, edge1_world)
|
|
1295
|
-
max_iter = edge_sdf_iter
|
|
1296
|
-
u = closest_edge_coordinate_box(geo_scale_a, edge0_a, edge1_a, max_iter)
|
|
1297
|
-
p_b_world = (1.0 - u) * edge0_world + u * edge1_world
|
|
1298
|
-
|
|
1299
|
-
# find closest point + contact normal on box A
|
|
1300
|
-
query_a = wp.transform_point(X_sw_a, p_b_world)
|
|
1301
|
-
p_a_body = closest_point_box(geo_scale_a, query_a)
|
|
1302
|
-
p_a_world = wp.transform_point(X_ws_a, p_a_body)
|
|
1303
|
-
query_b = wp.transform_point(X_sw_b, p_a_world)
|
|
1304
|
-
if wp.abs(query_b[0]) > plane_width or wp.abs(query_b[2]) > plane_length:
|
|
1305
|
-
# ensure that the closest point is actually inside the plane
|
|
1306
|
-
return
|
|
1307
|
-
diff = p_a_world - p_b_world
|
|
1308
|
-
com_a = wp.transform_get_translation(X_ws_a)
|
|
1309
|
-
query_b = wp.transform_point(X_sw_b, com_a)
|
|
1310
|
-
if wp.abs(query_b[0]) > plane_width or wp.abs(query_b[2]) > plane_length:
|
|
1311
|
-
# the COM is outside the plane
|
|
1312
|
-
normal = wp.normalize(com_a - p_b_world)
|
|
1313
|
-
else:
|
|
1314
|
-
normal = wp.transform_vector(X_ws_b, wp.vec3(0.0, 1.0, 0.0))
|
|
1315
|
-
distance = wp.dot(diff, normal)
|
|
1316
|
-
|
|
1317
|
-
elif geo_type_a == wp.sim.GEO_CAPSULE and geo_type_b == wp.sim.GEO_CAPSULE:
|
|
1318
|
-
# find closest edge coordinate to capsule SDF B
|
|
1319
|
-
half_height_a = geo_scale_a[1]
|
|
1320
|
-
half_height_b = geo_scale_b[1]
|
|
1321
|
-
# edge from capsule A
|
|
1322
|
-
# depending on point id, we query an edge from 0 to 0.5 or 0.5 to 1
|
|
1323
|
-
e0 = wp.vec3(0.0, half_height_a * float(point_id % 2), 0.0)
|
|
1324
|
-
e1 = wp.vec3(0.0, -half_height_a * float((point_id + 1) % 2), 0.0)
|
|
1325
|
-
edge0_world = wp.transform_point(X_ws_a, e0)
|
|
1326
|
-
edge1_world = wp.transform_point(X_ws_a, e1)
|
|
1327
|
-
edge0_b = wp.transform_point(X_sw_b, edge0_world)
|
|
1328
|
-
edge1_b = wp.transform_point(X_sw_b, edge1_world)
|
|
1329
|
-
max_iter = edge_sdf_iter
|
|
1330
|
-
u = closest_edge_coordinate_capsule(geo_scale_b[0], geo_scale_b[1], edge0_b, edge1_b, max_iter)
|
|
1331
|
-
p_a_world = (1.0 - u) * edge0_world + u * edge1_world
|
|
1332
|
-
p0_b_world = wp.transform_point(X_ws_b, wp.vec3(0.0, half_height_b, 0.0))
|
|
1333
|
-
p1_b_world = wp.transform_point(X_ws_b, wp.vec3(0.0, -half_height_b, 0.0))
|
|
1334
|
-
p_b_world = closest_point_line_segment(p0_b_world, p1_b_world, p_a_world)
|
|
1335
|
-
diff = p_a_world - p_b_world
|
|
1336
|
-
normal = wp.normalize(diff)
|
|
1337
|
-
distance = wp.dot(diff, normal)
|
|
1338
|
-
|
|
1339
|
-
elif geo_type_a == wp.sim.GEO_CAPSULE and geo_type_b == wp.sim.GEO_MESH:
|
|
1340
|
-
# find closest edge coordinate to mesh SDF B
|
|
1341
|
-
half_height_a = geo_scale_a[1]
|
|
1342
|
-
# edge from capsule A
|
|
1343
|
-
# depending on point id, we query an edge from -h to 0 or 0 to h
|
|
1344
|
-
e0 = wp.vec3(0.0, -half_height_a * float(point_id % 2), 0.0)
|
|
1345
|
-
e1 = wp.vec3(0.0, half_height_a * float((point_id + 1) % 2), 0.0)
|
|
1346
|
-
edge0_world = wp.transform_point(X_ws_a, e0)
|
|
1347
|
-
edge1_world = wp.transform_point(X_ws_a, e1)
|
|
1348
|
-
edge0_b = wp.transform_point(X_sw_b, edge0_world)
|
|
1349
|
-
edge1_b = wp.transform_point(X_sw_b, edge1_world)
|
|
1350
|
-
max_iter = edge_sdf_iter
|
|
1351
|
-
max_dist = (rigid_contact_margin + thickness) / min_scale_b
|
|
1352
|
-
mesh_b = geo.source[shape_b]
|
|
1353
|
-
u = closest_edge_coordinate_mesh(
|
|
1354
|
-
mesh_b, wp.cw_div(edge0_b, geo_scale_b), wp.cw_div(edge1_b, geo_scale_b), max_iter, max_dist
|
|
1355
|
-
)
|
|
1356
|
-
p_a_world = (1.0 - u) * edge0_world + u * edge1_world
|
|
1357
|
-
query_b_local = wp.transform_point(X_sw_b, p_a_world)
|
|
1358
|
-
mesh_b = geo.source[shape_b]
|
|
1359
|
-
|
|
1360
|
-
face_index = int(0)
|
|
1361
|
-
face_u = float(0.0)
|
|
1362
|
-
face_v = float(0.0)
|
|
1363
|
-
sign = float(0.0)
|
|
1364
|
-
res = wp.mesh_query_point_sign_normal(
|
|
1365
|
-
mesh_b, wp.cw_div(query_b_local, geo_scale_b), max_dist, sign, face_index, face_u, face_v
|
|
1366
|
-
)
|
|
1367
|
-
if res:
|
|
1368
|
-
shape_p = wp.mesh_eval_position(mesh_b, face_index, face_u, face_v)
|
|
1369
|
-
shape_p = wp.cw_mul(shape_p, geo_scale_b)
|
|
1370
|
-
p_b_world = wp.transform_point(X_ws_b, shape_p)
|
|
1371
|
-
p_a_world = closest_point_line_segment(edge0_world, edge1_world, p_b_world)
|
|
1372
|
-
# contact direction vector in world frame
|
|
1373
|
-
diff = p_a_world - p_b_world
|
|
1374
|
-
normal = wp.normalize(diff)
|
|
1375
|
-
distance = wp.dot(diff, normal)
|
|
1376
|
-
else:
|
|
1377
|
-
return
|
|
1378
|
-
|
|
1379
|
-
elif geo_type_a == wp.sim.GEO_MESH and geo_type_b == wp.sim.GEO_CAPSULE:
|
|
1380
|
-
# vertex-based contact
|
|
1381
|
-
mesh = wp.mesh_get(geo.source[shape_a])
|
|
1382
|
-
body_a_pos = wp.cw_mul(mesh.points[point_id], geo_scale_a)
|
|
1383
|
-
p_a_world = wp.transform_point(X_ws_a, body_a_pos)
|
|
1384
|
-
# find closest point + contact normal on capsule B
|
|
1385
|
-
half_height_b = geo_scale_b[1]
|
|
1386
|
-
A_b = wp.transform_point(X_ws_b, wp.vec3(0.0, half_height_b, 0.0))
|
|
1387
|
-
B_b = wp.transform_point(X_ws_b, wp.vec3(0.0, -half_height_b, 0.0))
|
|
1388
|
-
p_b_world = closest_point_line_segment(A_b, B_b, p_a_world)
|
|
1389
|
-
diff = p_a_world - p_b_world
|
|
1390
|
-
# this is more reliable in practice than using the SDF gradient
|
|
1391
|
-
normal = wp.normalize(diff)
|
|
1392
|
-
distance = wp.dot(diff, normal)
|
|
1393
|
-
|
|
1394
|
-
elif geo_type_a == wp.sim.GEO_CAPSULE and geo_type_b == wp.sim.GEO_PLANE:
|
|
1395
|
-
plane_width = geo_scale_b[0]
|
|
1396
|
-
plane_length = geo_scale_b[1]
|
|
1397
|
-
if point_id < 2:
|
|
1398
|
-
# vertex-based collision
|
|
1399
|
-
half_height_a = geo_scale_a[1]
|
|
1400
|
-
side = float(point_id) * 2.0 - 1.0
|
|
1401
|
-
p_a_world = wp.transform_point(X_ws_a, wp.vec3(0.0, side * half_height_a, 0.0))
|
|
1402
|
-
query_b = wp.transform_point(X_sw_b, p_a_world)
|
|
1403
|
-
p_b_body = closest_point_plane(geo_scale_b[0], geo_scale_b[1], query_b)
|
|
1404
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1405
|
-
diff = p_a_world - p_b_world
|
|
1406
|
-
if geo_scale_b[0] > 0.0 and geo_scale_b[1] > 0.0:
|
|
1407
|
-
normal = wp.normalize(diff)
|
|
1408
|
-
else:
|
|
1409
|
-
normal = wp.transform_vector(X_ws_b, wp.vec3(0.0, 1.0, 0.0))
|
|
1410
|
-
distance = wp.dot(diff, normal)
|
|
1411
|
-
else:
|
|
1412
|
-
# contact between capsule A and edges of finite plane B
|
|
1413
|
-
plane_width = geo_scale_b[0]
|
|
1414
|
-
plane_length = geo_scale_b[1]
|
|
1415
|
-
edge = get_plane_edge(point_id - 2, plane_width, plane_length)
|
|
1416
|
-
edge0_world = wp.transform_point(X_ws_b, wp.spatial_top(edge))
|
|
1417
|
-
edge1_world = wp.transform_point(X_ws_b, wp.spatial_bottom(edge))
|
|
1418
|
-
edge0_a = wp.transform_point(X_sw_a, edge0_world)
|
|
1419
|
-
edge1_a = wp.transform_point(X_sw_a, edge1_world)
|
|
1420
|
-
max_iter = edge_sdf_iter
|
|
1421
|
-
u = closest_edge_coordinate_capsule(geo_scale_a[0], geo_scale_a[1], edge0_a, edge1_a, max_iter)
|
|
1422
|
-
p_b_world = (1.0 - u) * edge0_world + u * edge1_world
|
|
1423
|
-
|
|
1424
|
-
# find closest point + contact normal on capsule A
|
|
1425
|
-
half_height_a = geo_scale_a[1]
|
|
1426
|
-
p0_a_world = wp.transform_point(X_ws_a, wp.vec3(0.0, half_height_a, 0.0))
|
|
1427
|
-
p1_a_world = wp.transform_point(X_ws_a, wp.vec3(0.0, -half_height_a, 0.0))
|
|
1428
|
-
p_a_world = closest_point_line_segment(p0_a_world, p1_a_world, p_b_world)
|
|
1429
|
-
diff = p_a_world - p_b_world
|
|
1430
|
-
# normal = wp.transform_vector(X_ws_b, wp.vec3(0.0, 1.0, 0.0))
|
|
1431
|
-
normal = wp.normalize(diff)
|
|
1432
|
-
distance = wp.dot(diff, normal)
|
|
1433
|
-
|
|
1434
|
-
elif geo_type_a == wp.sim.GEO_MESH and geo_type_b == wp.sim.GEO_BOX:
|
|
1435
|
-
# vertex-based contact
|
|
1436
|
-
mesh = wp.mesh_get(geo.source[shape_a])
|
|
1437
|
-
body_a_pos = wp.cw_mul(mesh.points[point_id], geo_scale_a)
|
|
1438
|
-
p_a_world = wp.transform_point(X_ws_a, body_a_pos)
|
|
1439
|
-
# find closest point + contact normal on box B
|
|
1440
|
-
query_b = wp.transform_point(X_sw_b, p_a_world)
|
|
1441
|
-
p_b_body = closest_point_box(geo_scale_b, query_b)
|
|
1442
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1443
|
-
diff = p_a_world - p_b_world
|
|
1444
|
-
# this is more reliable in practice than using the SDF gradient
|
|
1445
|
-
normal = wp.normalize(diff)
|
|
1446
|
-
if box_sdf(geo_scale_b, query_b) < 0.0:
|
|
1447
|
-
normal = -normal
|
|
1448
|
-
distance = wp.dot(diff, normal)
|
|
1449
|
-
|
|
1450
|
-
elif geo_type_a == wp.sim.GEO_BOX and geo_type_b == wp.sim.GEO_MESH:
|
|
1451
|
-
# vertex-based contact
|
|
1452
|
-
query_a = get_box_vertex(point_id, geo_scale_a)
|
|
1453
|
-
p_a_world = wp.transform_point(X_ws_a, query_a)
|
|
1454
|
-
query_b_local = wp.transform_point(X_sw_b, p_a_world)
|
|
1455
|
-
mesh_b = geo.source[shape_b]
|
|
1456
|
-
max_dist = (rigid_contact_margin + thickness) / min_scale_b
|
|
1457
|
-
face_index = int(0)
|
|
1458
|
-
face_u = float(0.0)
|
|
1459
|
-
face_v = float(0.0)
|
|
1460
|
-
sign = float(0.0)
|
|
1461
|
-
res = wp.mesh_query_point_sign_normal(
|
|
1462
|
-
mesh_b, wp.cw_div(query_b_local, geo_scale_b), max_dist, sign, face_index, face_u, face_v
|
|
1463
|
-
)
|
|
1464
|
-
|
|
1465
|
-
if res:
|
|
1466
|
-
shape_p = wp.mesh_eval_position(mesh_b, face_index, face_u, face_v)
|
|
1467
|
-
shape_p = wp.cw_mul(shape_p, geo_scale_b)
|
|
1468
|
-
p_b_world = wp.transform_point(X_ws_b, shape_p)
|
|
1469
|
-
# contact direction vector in world frame
|
|
1470
|
-
diff_b = p_a_world - p_b_world
|
|
1471
|
-
normal = wp.normalize(diff_b) * sign
|
|
1472
|
-
distance = wp.dot(diff_b, normal)
|
|
1473
|
-
else:
|
|
1474
|
-
return
|
|
1475
|
-
|
|
1476
|
-
elif geo_type_a == wp.sim.GEO_MESH and geo_type_b == wp.sim.GEO_MESH:
|
|
1477
|
-
# vertex-based contact
|
|
1478
|
-
mesh = wp.mesh_get(geo.source[shape_a])
|
|
1479
|
-
mesh_b = geo.source[shape_b]
|
|
1480
|
-
|
|
1481
|
-
body_a_pos = wp.cw_mul(mesh.points[point_id], geo_scale_a)
|
|
1482
|
-
p_a_world = wp.transform_point(X_ws_a, body_a_pos)
|
|
1483
|
-
query_b_local = wp.transform_point(X_sw_b, p_a_world)
|
|
1484
|
-
|
|
1485
|
-
face_index = int(0)
|
|
1486
|
-
face_u = float(0.0)
|
|
1487
|
-
face_v = float(0.0)
|
|
1488
|
-
sign = float(0.0)
|
|
1489
|
-
min_scale = min(min_scale_a, min_scale_b)
|
|
1490
|
-
max_dist = (rigid_contact_margin + thickness) / min_scale
|
|
1491
|
-
|
|
1492
|
-
res = wp.mesh_query_point_sign_normal(
|
|
1493
|
-
mesh_b, wp.cw_div(query_b_local, geo_scale_b), max_dist, sign, face_index, face_u, face_v
|
|
1494
|
-
)
|
|
1495
|
-
|
|
1496
|
-
if res:
|
|
1497
|
-
shape_p = wp.mesh_eval_position(mesh_b, face_index, face_u, face_v)
|
|
1498
|
-
shape_p = wp.cw_mul(shape_p, geo_scale_b)
|
|
1499
|
-
p_b_world = wp.transform_point(X_ws_b, shape_p)
|
|
1500
|
-
# contact direction vector in world frame
|
|
1501
|
-
diff_b = p_a_world - p_b_world
|
|
1502
|
-
normal = wp.normalize(diff_b) * sign
|
|
1503
|
-
distance = wp.dot(diff_b, normal)
|
|
1504
|
-
else:
|
|
1505
|
-
return
|
|
1506
|
-
|
|
1507
|
-
elif geo_type_a == wp.sim.GEO_MESH and geo_type_b == wp.sim.GEO_PLANE:
|
|
1508
|
-
# vertex-based contact
|
|
1509
|
-
mesh = wp.mesh_get(geo.source[shape_a])
|
|
1510
|
-
body_a_pos = wp.cw_mul(mesh.points[point_id], geo_scale_a)
|
|
1511
|
-
p_a_world = wp.transform_point(X_ws_a, body_a_pos)
|
|
1512
|
-
query_b = wp.transform_point(X_sw_b, p_a_world)
|
|
1513
|
-
p_b_body = closest_point_plane(geo_scale_b[0], geo_scale_b[1], query_b)
|
|
1514
|
-
p_b_world = wp.transform_point(X_ws_b, p_b_body)
|
|
1515
|
-
diff = p_a_world - p_b_world
|
|
1516
|
-
|
|
1517
|
-
# if the plane is infinite or the point is within the plane we fix the normal to prevent intersections
|
|
1518
|
-
if (geo_scale_b[0] == 0.0 and geo_scale_b[1] == 0.0) or (
|
|
1519
|
-
wp.abs(query_b[0]) < geo_scale_b[0] and wp.abs(query_b[2]) < geo_scale_b[1]
|
|
1520
|
-
):
|
|
1521
|
-
normal = wp.transform_vector(X_ws_b, wp.vec3(0.0, 1.0, 0.0))
|
|
1522
|
-
distance = wp.dot(diff, normal)
|
|
1523
|
-
else:
|
|
1524
|
-
normal = wp.normalize(diff)
|
|
1525
|
-
distance = wp.dot(diff, normal)
|
|
1526
|
-
# ignore extreme penetrations (e.g. when mesh is below the plane)
|
|
1527
|
-
if distance < -rigid_contact_margin:
|
|
1528
|
-
return
|
|
1529
|
-
|
|
1530
|
-
else:
|
|
1531
|
-
print("Unsupported geometry pair in collision handling")
|
|
1532
|
-
return
|
|
1533
|
-
|
|
1534
|
-
d = distance - thickness
|
|
1535
|
-
if d < rigid_contact_margin:
|
|
1536
|
-
if contact_pairwise_counter:
|
|
1537
|
-
pair_contact_id = limited_counter_increment(
|
|
1538
|
-
contact_pairwise_counter, pair_index, contact_tids, tid, contact_limit
|
|
1539
|
-
)
|
|
1540
|
-
if pair_contact_id == -1:
|
|
1541
|
-
# wp.printf("Reached contact point limit %d >= %d for shape pair %d and %d (pair_index: %d)\n",
|
|
1542
|
-
# contact_pairwise_counter[pair_index], contact_limit, shape_a, shape_b, pair_index)
|
|
1543
|
-
# reached contact point limit
|
|
1544
|
-
return
|
|
1545
|
-
index = counter_increment(contact_count, 0, contact_tids, tid)
|
|
1546
|
-
if index == -1:
|
|
1547
|
-
return
|
|
1548
|
-
contact_shape0[index] = shape_a
|
|
1549
|
-
contact_shape1[index] = shape_b
|
|
1550
|
-
# transform from world into body frame (so the contact point includes the shape transform)
|
|
1551
|
-
contact_point0[index] = wp.transform_point(X_bw_a, p_a_world)
|
|
1552
|
-
contact_point1[index] = wp.transform_point(X_bw_b, p_b_world)
|
|
1553
|
-
contact_offset0[index] = wp.transform_vector(X_bw_a, -thickness_a * normal)
|
|
1554
|
-
contact_offset1[index] = wp.transform_vector(X_bw_b, thickness_b * normal)
|
|
1555
|
-
contact_normal[index] = normal
|
|
1556
|
-
contact_thickness[index] = thickness
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
def collide(
|
|
1560
|
-
model: Model,
|
|
1561
|
-
state: State,
|
|
1562
|
-
edge_sdf_iter: int = 10,
|
|
1563
|
-
iterate_mesh_vertices: bool = True,
|
|
1564
|
-
requires_grad: Optional[bool] = None,
|
|
1565
|
-
) -> None:
|
|
1566
|
-
"""Generate contact points for the particles and rigid bodies in the model for use in contact-dynamics kernels.
|
|
1567
|
-
|
|
1568
|
-
Args:
|
|
1569
|
-
model: The model to be simulated.
|
|
1570
|
-
state: The state of the model.
|
|
1571
|
-
edge_sdf_iter: Number of search iterations for finding closest contact points between edges and SDF.
|
|
1572
|
-
iterate_mesh_vertices: Whether to iterate over all vertices of a mesh for contact generation
|
|
1573
|
-
(used for capsule/box <> mesh collision).
|
|
1574
|
-
requires_grad: Whether to duplicate contact arrays for gradient computation
|
|
1575
|
-
(if ``None``, uses ``model.requires_grad``).
|
|
1576
|
-
"""
|
|
1577
|
-
|
|
1578
|
-
if requires_grad is None:
|
|
1579
|
-
requires_grad = model.requires_grad
|
|
1580
|
-
|
|
1581
|
-
with wp.ScopedTimer("collide", False):
|
|
1582
|
-
# generate soft contacts for particles and shapes except ground plane (last shape)
|
|
1583
|
-
if model.particle_count and model.shape_count > 1:
|
|
1584
|
-
if requires_grad:
|
|
1585
|
-
model.soft_contact_body_pos = wp.empty_like(model.soft_contact_body_pos)
|
|
1586
|
-
model.soft_contact_body_vel = wp.empty_like(model.soft_contact_body_vel)
|
|
1587
|
-
model.soft_contact_normal = wp.empty_like(model.soft_contact_normal)
|
|
1588
|
-
# clear old count
|
|
1589
|
-
model.soft_contact_count.zero_()
|
|
1590
|
-
wp.launch(
|
|
1591
|
-
kernel=create_soft_contacts,
|
|
1592
|
-
dim=model.particle_count * (model.shape_count - 1),
|
|
1593
|
-
inputs=[
|
|
1594
|
-
state.particle_q,
|
|
1595
|
-
model.particle_radius,
|
|
1596
|
-
model.particle_flags,
|
|
1597
|
-
state.body_q,
|
|
1598
|
-
model.shape_transform,
|
|
1599
|
-
model.shape_body,
|
|
1600
|
-
model.shape_geo,
|
|
1601
|
-
model.soft_contact_margin,
|
|
1602
|
-
model.soft_contact_max,
|
|
1603
|
-
model.shape_count - 1,
|
|
1604
|
-
],
|
|
1605
|
-
outputs=[
|
|
1606
|
-
model.soft_contact_count,
|
|
1607
|
-
model.soft_contact_particle,
|
|
1608
|
-
model.soft_contact_shape,
|
|
1609
|
-
model.soft_contact_body_pos,
|
|
1610
|
-
model.soft_contact_body_vel,
|
|
1611
|
-
model.soft_contact_normal,
|
|
1612
|
-
model.soft_contact_tids,
|
|
1613
|
-
],
|
|
1614
|
-
device=model.device,
|
|
1615
|
-
)
|
|
1616
|
-
|
|
1617
|
-
if model.shape_contact_pair_count or (model.ground and model.shape_ground_contact_pair_count):
|
|
1618
|
-
# clear old count
|
|
1619
|
-
model.rigid_contact_count.zero_()
|
|
1620
|
-
|
|
1621
|
-
model.rigid_contact_broad_shape0.fill_(-1)
|
|
1622
|
-
model.rigid_contact_broad_shape1.fill_(-1)
|
|
1623
|
-
|
|
1624
|
-
if model.shape_contact_pair_count:
|
|
1625
|
-
wp.launch(
|
|
1626
|
-
kernel=broadphase_collision_pairs,
|
|
1627
|
-
dim=model.shape_contact_pair_count,
|
|
1628
|
-
inputs=[
|
|
1629
|
-
model.shape_contact_pairs,
|
|
1630
|
-
state.body_q,
|
|
1631
|
-
model.shape_transform,
|
|
1632
|
-
model.shape_body,
|
|
1633
|
-
model.body_mass,
|
|
1634
|
-
model.shape_count,
|
|
1635
|
-
model.shape_geo,
|
|
1636
|
-
model.shape_collision_radius,
|
|
1637
|
-
model.rigid_contact_max,
|
|
1638
|
-
model.rigid_contact_margin,
|
|
1639
|
-
model.rigid_mesh_contact_max,
|
|
1640
|
-
iterate_mesh_vertices,
|
|
1641
|
-
],
|
|
1642
|
-
outputs=[
|
|
1643
|
-
model.rigid_contact_count,
|
|
1644
|
-
model.rigid_contact_broad_shape0,
|
|
1645
|
-
model.rigid_contact_broad_shape1,
|
|
1646
|
-
model.rigid_contact_point_id,
|
|
1647
|
-
model.rigid_contact_point_limit,
|
|
1648
|
-
],
|
|
1649
|
-
device=model.device,
|
|
1650
|
-
record_tape=False,
|
|
1651
|
-
)
|
|
1652
|
-
|
|
1653
|
-
if model.ground and model.shape_ground_contact_pair_count:
|
|
1654
|
-
wp.launch(
|
|
1655
|
-
kernel=broadphase_collision_pairs,
|
|
1656
|
-
dim=model.shape_ground_contact_pair_count,
|
|
1657
|
-
inputs=[
|
|
1658
|
-
model.shape_ground_contact_pairs,
|
|
1659
|
-
state.body_q,
|
|
1660
|
-
model.shape_transform,
|
|
1661
|
-
model.shape_body,
|
|
1662
|
-
model.body_mass,
|
|
1663
|
-
model.shape_count,
|
|
1664
|
-
model.shape_geo,
|
|
1665
|
-
model.shape_collision_radius,
|
|
1666
|
-
model.rigid_contact_max,
|
|
1667
|
-
model.rigid_contact_margin,
|
|
1668
|
-
model.rigid_mesh_contact_max,
|
|
1669
|
-
iterate_mesh_vertices,
|
|
1670
|
-
],
|
|
1671
|
-
outputs=[
|
|
1672
|
-
model.rigid_contact_count,
|
|
1673
|
-
model.rigid_contact_broad_shape0,
|
|
1674
|
-
model.rigid_contact_broad_shape1,
|
|
1675
|
-
model.rigid_contact_point_id,
|
|
1676
|
-
model.rigid_contact_point_limit,
|
|
1677
|
-
],
|
|
1678
|
-
device=model.device,
|
|
1679
|
-
record_tape=False,
|
|
1680
|
-
)
|
|
1681
|
-
|
|
1682
|
-
if model.shape_contact_pair_count or (model.ground and model.shape_ground_contact_pair_count):
|
|
1683
|
-
if requires_grad:
|
|
1684
|
-
model.rigid_contact_point0 = wp.empty_like(model.rigid_contact_point0)
|
|
1685
|
-
model.rigid_contact_point1 = wp.empty_like(model.rigid_contact_point1)
|
|
1686
|
-
model.rigid_contact_offset0 = wp.empty_like(model.rigid_contact_offset0)
|
|
1687
|
-
model.rigid_contact_offset1 = wp.empty_like(model.rigid_contact_offset1)
|
|
1688
|
-
model.rigid_contact_normal = wp.empty_like(model.rigid_contact_normal)
|
|
1689
|
-
model.rigid_contact_thickness = wp.empty_like(model.rigid_contact_thickness)
|
|
1690
|
-
model.rigid_contact_count = wp.zeros_like(model.rigid_contact_count)
|
|
1691
|
-
model.rigid_contact_tids = wp.full_like(model.rigid_contact_tids, -1)
|
|
1692
|
-
model.rigid_contact_shape0 = wp.empty_like(model.rigid_contact_shape0)
|
|
1693
|
-
model.rigid_contact_shape1 = wp.empty_like(model.rigid_contact_shape1)
|
|
1694
|
-
|
|
1695
|
-
if model.rigid_contact_pairwise_counter is not None:
|
|
1696
|
-
model.rigid_contact_pairwise_counter = wp.zeros_like(model.rigid_contact_pairwise_counter)
|
|
1697
|
-
else:
|
|
1698
|
-
model.rigid_contact_count.zero_()
|
|
1699
|
-
model.rigid_contact_tids.fill_(-1)
|
|
1700
|
-
|
|
1701
|
-
if model.rigid_contact_pairwise_counter is not None:
|
|
1702
|
-
model.rigid_contact_pairwise_counter.zero_()
|
|
1703
|
-
|
|
1704
|
-
model.rigid_contact_shape0.fill_(-1)
|
|
1705
|
-
model.rigid_contact_shape1.fill_(-1)
|
|
1706
|
-
|
|
1707
|
-
wp.launch(
|
|
1708
|
-
kernel=handle_contact_pairs,
|
|
1709
|
-
dim=model.rigid_contact_max,
|
|
1710
|
-
inputs=[
|
|
1711
|
-
state.body_q,
|
|
1712
|
-
model.shape_transform,
|
|
1713
|
-
model.shape_body,
|
|
1714
|
-
model.shape_geo,
|
|
1715
|
-
model.rigid_contact_margin,
|
|
1716
|
-
model.rigid_contact_broad_shape0,
|
|
1717
|
-
model.rigid_contact_broad_shape1,
|
|
1718
|
-
model.shape_count,
|
|
1719
|
-
model.rigid_contact_point_id,
|
|
1720
|
-
model.rigid_contact_point_limit,
|
|
1721
|
-
edge_sdf_iter,
|
|
1722
|
-
],
|
|
1723
|
-
outputs=[
|
|
1724
|
-
model.rigid_contact_count,
|
|
1725
|
-
model.rigid_contact_shape0,
|
|
1726
|
-
model.rigid_contact_shape1,
|
|
1727
|
-
model.rigid_contact_point0,
|
|
1728
|
-
model.rigid_contact_point1,
|
|
1729
|
-
model.rigid_contact_offset0,
|
|
1730
|
-
model.rigid_contact_offset1,
|
|
1731
|
-
model.rigid_contact_normal,
|
|
1732
|
-
model.rigid_contact_thickness,
|
|
1733
|
-
model.rigid_contact_pairwise_counter,
|
|
1734
|
-
model.rigid_contact_tids,
|
|
1735
|
-
],
|
|
1736
|
-
device=model.device,
|
|
1737
|
-
)
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
@wp.func
|
|
1741
|
-
def compute_tri_aabb(
|
|
1742
|
-
v1: wp.vec3,
|
|
1743
|
-
v2: wp.vec3,
|
|
1744
|
-
v3: wp.vec3,
|
|
1745
|
-
):
|
|
1746
|
-
lower = wp.min(wp.min(v1, v2), v3)
|
|
1747
|
-
upper = wp.max(wp.max(v1, v2), v3)
|
|
1748
|
-
|
|
1749
|
-
return lower, upper
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
@wp.kernel
|
|
1753
|
-
def compute_tri_aabbs(
|
|
1754
|
-
pos: wp.array(dtype=wp.vec3),
|
|
1755
|
-
tri_indices: wp.array(dtype=wp.int32, ndim=2),
|
|
1756
|
-
lower_bounds: wp.array(dtype=wp.vec3),
|
|
1757
|
-
upper_bounds: wp.array(dtype=wp.vec3),
|
|
1758
|
-
):
|
|
1759
|
-
t_id = wp.tid()
|
|
1760
|
-
|
|
1761
|
-
v1 = pos[tri_indices[t_id, 0]]
|
|
1762
|
-
v2 = pos[tri_indices[t_id, 1]]
|
|
1763
|
-
v3 = pos[tri_indices[t_id, 2]]
|
|
1764
|
-
|
|
1765
|
-
lower, upper = compute_tri_aabb(v1, v2, v3)
|
|
1766
|
-
|
|
1767
|
-
lower_bounds[t_id] = lower
|
|
1768
|
-
upper_bounds[t_id] = upper
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
@wp.kernel
|
|
1772
|
-
def compute_edge_aabbs(
|
|
1773
|
-
pos: wp.array(dtype=wp.vec3),
|
|
1774
|
-
edge_indices: wp.array(dtype=wp.int32, ndim=2),
|
|
1775
|
-
lower_bounds: wp.array(dtype=wp.vec3),
|
|
1776
|
-
upper_bounds: wp.array(dtype=wp.vec3),
|
|
1777
|
-
):
|
|
1778
|
-
e_id = wp.tid()
|
|
1779
|
-
|
|
1780
|
-
v1 = pos[edge_indices[e_id, 2]]
|
|
1781
|
-
v2 = pos[edge_indices[e_id, 3]]
|
|
1782
|
-
|
|
1783
|
-
lower_bounds[e_id] = wp.min(v1, v2)
|
|
1784
|
-
upper_bounds[e_id] = wp.max(v1, v2)
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
@wp.func
|
|
1788
|
-
def tri_is_neighbor(a_1: wp.int32, a_2: wp.int32, a_3: wp.int32, b_1: wp.int32, b_2: wp.int32, b_3: wp.int32):
|
|
1789
|
-
tri_is_neighbor = (
|
|
1790
|
-
a_1 == b_1
|
|
1791
|
-
or a_1 == b_2
|
|
1792
|
-
or a_1 == b_3
|
|
1793
|
-
or a_2 == b_1
|
|
1794
|
-
or a_2 == b_2
|
|
1795
|
-
or a_2 == b_3
|
|
1796
|
-
or a_3 == b_1
|
|
1797
|
-
or a_3 == b_2
|
|
1798
|
-
or a_3 == b_3
|
|
1799
|
-
)
|
|
1800
|
-
|
|
1801
|
-
return tri_is_neighbor
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
@wp.func
|
|
1805
|
-
def vertex_adjacent_to_triangle(v: wp.int32, a: wp.int32, b: wp.int32, c: wp.int32):
|
|
1806
|
-
return v == a or v == b or v == c
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
@wp.kernel
|
|
1810
|
-
def init_triangle_collision_data_kernel(
|
|
1811
|
-
query_radius: float,
|
|
1812
|
-
# outputs
|
|
1813
|
-
triangle_colliding_vertices_count: wp.array(dtype=wp.int32),
|
|
1814
|
-
triangle_colliding_vertices_min_dist: wp.array(dtype=float),
|
|
1815
|
-
resize_flags: wp.array(dtype=wp.int32),
|
|
1816
|
-
):
|
|
1817
|
-
tri_index = wp.tid()
|
|
1818
|
-
|
|
1819
|
-
triangle_colliding_vertices_count[tri_index] = 0
|
|
1820
|
-
triangle_colliding_vertices_min_dist[tri_index] = query_radius
|
|
1821
|
-
|
|
1822
|
-
if tri_index == 0:
|
|
1823
|
-
for i in range(3):
|
|
1824
|
-
resize_flags[i] = 0
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
@wp.kernel
|
|
1828
|
-
def vertex_triangle_collision_detection_kernel(
|
|
1829
|
-
query_radius: float,
|
|
1830
|
-
bvh_id: wp.uint64,
|
|
1831
|
-
pos: wp.array(dtype=wp.vec3),
|
|
1832
|
-
tri_indices: wp.array(dtype=wp.int32, ndim=2),
|
|
1833
|
-
vertex_colliding_triangles_offsets: wp.array(dtype=wp.int32),
|
|
1834
|
-
vertex_colliding_triangles_buffer_sizes: wp.array(dtype=wp.int32),
|
|
1835
|
-
triangle_colliding_vertices_offsets: wp.array(dtype=wp.int32),
|
|
1836
|
-
triangle_colliding_vertices_buffer_sizes: wp.array(dtype=wp.int32),
|
|
1837
|
-
# outputs
|
|
1838
|
-
vertex_colliding_triangles: wp.array(dtype=wp.int32),
|
|
1839
|
-
vertex_colliding_triangles_count: wp.array(dtype=wp.int32),
|
|
1840
|
-
vertex_colliding_triangles_min_dist: wp.array(dtype=float),
|
|
1841
|
-
triangle_colliding_vertices: wp.array(dtype=wp.int32),
|
|
1842
|
-
triangle_colliding_vertices_count: wp.array(dtype=wp.int32),
|
|
1843
|
-
triangle_colliding_vertices_min_dist: wp.array(dtype=float),
|
|
1844
|
-
resize_flags: wp.array(dtype=wp.int32),
|
|
1845
|
-
):
|
|
1846
|
-
"""
|
|
1847
|
-
This function applies discrete collision detection between vertices and triangles. It uses pre-allocated spaces to
|
|
1848
|
-
record the collision data. This collision detector works both ways, i.e., it records vertices' colliding triangles to
|
|
1849
|
-
`vertex_colliding_triangles`, and records each triangles colliding vertices to `triangle_colliding_vertices`.
|
|
1850
|
-
|
|
1851
|
-
This function assumes that all the vertices are on triangles, and can be indexed from the pos argument.
|
|
1852
|
-
|
|
1853
|
-
Note:
|
|
1854
|
-
|
|
1855
|
-
The collision date buffer is pre-allocated and cannot be changed during collision detection, therefore, the space
|
|
1856
|
-
may not be enough. If the space is not enough to record all the collision information, the function will set a
|
|
1857
|
-
certain element in resized_flag to be true. The user can reallocate the buffer based on vertex_colliding_triangles_count
|
|
1858
|
-
and vertex_colliding_triangles_count.
|
|
1859
|
-
|
|
1860
|
-
Attributes:
|
|
1861
|
-
bvh_id (int): the bvh id you want to collide with
|
|
1862
|
-
query_radius (float): the contact radius. vertex-triangle pairs whose distance are less than this will get detected
|
|
1863
|
-
pos (array): positions of all the vertices that make up triangles
|
|
1864
|
-
vertex_colliding_triangles (array): flattened buffer of vertices' collision triangles
|
|
1865
|
-
vertex_colliding_triangles_count (array): number of triangles each vertex collides
|
|
1866
|
-
vertex_colliding_triangles_offsets (array): where each vertex' collision buffer starts
|
|
1867
|
-
vertex_colliding_triangles_buffer_sizes (array): size of each vertex' collision buffer, will be modified if resizing is needed
|
|
1868
|
-
vertex_colliding_triangles_min_dist (array): each vertex' min distance to all (non-neighbor) triangles
|
|
1869
|
-
triangle_colliding_vertices (array): positions of all the triangles' collision vertices, every two elements
|
|
1870
|
-
records the vertex index and a triangle index it collides to
|
|
1871
|
-
triangle_colliding_vertices_count (array): number of triangles each vertex collides
|
|
1872
|
-
triangle_colliding_vertices_offsets (array): where each triangle's collision buffer starts
|
|
1873
|
-
triangle_colliding_vertices_buffer_sizes (array): size of each triangle's collision buffer, will be modified if resizing is needed
|
|
1874
|
-
triangle_colliding_vertices_min_dist (array): each triangle's min distance to all (non-self) vertices
|
|
1875
|
-
resized_flag (array): size == 3, (vertex_buffer_resize_required, triangle_buffer_resize_required, edge_buffer_resize_required)
|
|
1876
|
-
"""
|
|
1877
|
-
|
|
1878
|
-
v_index = wp.tid()
|
|
1879
|
-
v = pos[v_index]
|
|
1880
|
-
vertex_buffer_offset = vertex_colliding_triangles_offsets[v_index]
|
|
1881
|
-
vertex_buffer_size = vertex_colliding_triangles_offsets[v_index + 1] - vertex_buffer_offset
|
|
1882
|
-
|
|
1883
|
-
lower = wp.vec3(v[0] - query_radius, v[1] - query_radius, v[2] - query_radius)
|
|
1884
|
-
upper = wp.vec3(v[0] + query_radius, v[1] + query_radius, v[2] + query_radius)
|
|
1885
|
-
|
|
1886
|
-
query = wp.bvh_query_aabb(bvh_id, lower, upper)
|
|
1887
|
-
|
|
1888
|
-
tri_index = wp.int32(0)
|
|
1889
|
-
vertex_num_collisions = wp.int32(0)
|
|
1890
|
-
min_dis_to_tris = query_radius
|
|
1891
|
-
while wp.bvh_query_next(query, tri_index):
|
|
1892
|
-
t1 = tri_indices[tri_index, 0]
|
|
1893
|
-
t2 = tri_indices[tri_index, 1]
|
|
1894
|
-
t3 = tri_indices[tri_index, 2]
|
|
1895
|
-
if vertex_adjacent_to_triangle(v_index, t1, t2, t3):
|
|
1896
|
-
continue
|
|
1897
|
-
|
|
1898
|
-
u1 = pos[t1]
|
|
1899
|
-
u2 = pos[t2]
|
|
1900
|
-
u3 = pos[t3]
|
|
1901
|
-
|
|
1902
|
-
closest_p, bary, feature_type = triangle_closest_point(u1, u2, u3, v)
|
|
1903
|
-
|
|
1904
|
-
dist = wp.length(closest_p - v)
|
|
1905
|
-
|
|
1906
|
-
if dist < query_radius:
|
|
1907
|
-
# record v-f collision to vertex
|
|
1908
|
-
min_dis_to_tris = wp.min(min_dis_to_tris, dist)
|
|
1909
|
-
if vertex_num_collisions < vertex_buffer_size:
|
|
1910
|
-
vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions)] = v_index
|
|
1911
|
-
vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions) + 1] = tri_index
|
|
1912
|
-
else:
|
|
1913
|
-
resize_flags[VERTEX_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
|
|
1914
|
-
|
|
1915
|
-
vertex_num_collisions = vertex_num_collisions + 1
|
|
1916
|
-
|
|
1917
|
-
wp.atomic_min(triangle_colliding_vertices_min_dist, tri_index, dist)
|
|
1918
|
-
tri_buffer_size = triangle_colliding_vertices_buffer_sizes[tri_index]
|
|
1919
|
-
tri_num_collisions = wp.atomic_add(triangle_colliding_vertices_count, tri_index, 1)
|
|
1920
|
-
|
|
1921
|
-
if tri_num_collisions < tri_buffer_size:
|
|
1922
|
-
tri_buffer_offset = triangle_colliding_vertices_offsets[tri_index]
|
|
1923
|
-
# record v-f collision to triangle
|
|
1924
|
-
triangle_colliding_vertices[tri_buffer_offset + tri_num_collisions] = v_index
|
|
1925
|
-
else:
|
|
1926
|
-
resize_flags[TRI_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
|
|
1927
|
-
|
|
1928
|
-
vertex_colliding_triangles_count[v_index] = vertex_num_collisions
|
|
1929
|
-
vertex_colliding_triangles_min_dist[v_index] = min_dis_to_tris
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
@wp.kernel
|
|
1933
|
-
def vertex_triangle_collision_detection_no_triangle_buffers_kernel(
|
|
1934
|
-
query_radius: float,
|
|
1935
|
-
bvh_id: wp.uint64,
|
|
1936
|
-
pos: wp.array(dtype=wp.vec3),
|
|
1937
|
-
tri_indices: wp.array(dtype=wp.int32, ndim=2),
|
|
1938
|
-
vertex_colliding_triangles_offsets: wp.array(dtype=wp.int32),
|
|
1939
|
-
vertex_colliding_triangles_buffer_sizes: wp.array(dtype=wp.int32),
|
|
1940
|
-
# outputs
|
|
1941
|
-
vertex_colliding_triangles: wp.array(dtype=wp.int32),
|
|
1942
|
-
vertex_colliding_triangles_count: wp.array(dtype=wp.int32),
|
|
1943
|
-
vertex_colliding_triangles_min_dist: wp.array(dtype=float),
|
|
1944
|
-
triangle_colliding_vertices_min_dist: wp.array(dtype=float),
|
|
1945
|
-
resize_flags: wp.array(dtype=wp.int32),
|
|
1946
|
-
):
|
|
1947
|
-
"""
|
|
1948
|
-
This function applies discrete collision detection between vertices and triangles. It uses pre-allocated spaces to
|
|
1949
|
-
record the collision data. Unlike `vertex_triangle_collision_detection_kernel`, this collision detection kernel
|
|
1950
|
-
works only in one way, i.e., it only records vertices' colliding triangles to `vertex_colliding_triangles`.
|
|
1951
|
-
|
|
1952
|
-
This function assumes that all the vertices are on triangles, and can be indexed from the pos argument.
|
|
1953
|
-
|
|
1954
|
-
Note:
|
|
1955
|
-
|
|
1956
|
-
The collision date buffer is pre-allocated and cannot be changed during collision detection, therefore, the space
|
|
1957
|
-
may not be enough. If the space is not enough to record all the collision information, the function will set a
|
|
1958
|
-
certain element in resized_flag to be true. The user can reallocate the buffer based on vertex_colliding_triangles_count
|
|
1959
|
-
and vertex_colliding_triangles_count.
|
|
1960
|
-
|
|
1961
|
-
Attributes:
|
|
1962
|
-
bvh_id (int): the bvh id you want to collide with
|
|
1963
|
-
query_radius (float): the contact radius. vertex-triangle pairs whose distance are less than this will get detected
|
|
1964
|
-
pos (array): positions of all the vertices that make up triangles
|
|
1965
|
-
vertex_colliding_triangles (array): flattened buffer of vertices' collision triangles, every two elements records
|
|
1966
|
-
the vertex index and a triangle index it collides to
|
|
1967
|
-
vertex_colliding_triangles_count (array): number of triangles each vertex collides
|
|
1968
|
-
vertex_colliding_triangles_offsets (array): where each vertex' collision buffer starts
|
|
1969
|
-
vertex_colliding_triangles_buffer_sizes (array): size of each vertex' collision buffer, will be modified if resizing is needed
|
|
1970
|
-
vertex_colliding_triangles_min_dist (array): each vertex' min distance to all (non-neighbor) triangles
|
|
1971
|
-
triangle_colliding_vertices_min_dist (array): each triangle's min distance to all (non-self) vertices
|
|
1972
|
-
resized_flag (array): size == 3, (vertex_buffer_resize_required, triangle_buffer_resize_required, edge_buffer_resize_required)
|
|
1973
|
-
"""
|
|
1974
|
-
|
|
1975
|
-
v_index = wp.tid()
|
|
1976
|
-
v = pos[v_index]
|
|
1977
|
-
vertex_buffer_offset = vertex_colliding_triangles_offsets[v_index]
|
|
1978
|
-
vertex_buffer_size = vertex_colliding_triangles_offsets[v_index + 1] - vertex_buffer_offset
|
|
1979
|
-
|
|
1980
|
-
lower = wp.vec3(v[0] - query_radius, v[1] - query_radius, v[2] - query_radius)
|
|
1981
|
-
upper = wp.vec3(v[0] + query_radius, v[1] + query_radius, v[2] + query_radius)
|
|
1982
|
-
|
|
1983
|
-
query = wp.bvh_query_aabb(bvh_id, lower, upper)
|
|
1984
|
-
|
|
1985
|
-
tri_index = wp.int32(0)
|
|
1986
|
-
vertex_num_collisions = wp.int32(0)
|
|
1987
|
-
min_dis_to_tris = query_radius
|
|
1988
|
-
while wp.bvh_query_next(query, tri_index):
|
|
1989
|
-
t1 = tri_indices[tri_index, 0]
|
|
1990
|
-
t2 = tri_indices[tri_index, 1]
|
|
1991
|
-
t3 = tri_indices[tri_index, 2]
|
|
1992
|
-
if vertex_adjacent_to_triangle(v_index, t1, t2, t3):
|
|
1993
|
-
continue
|
|
1994
|
-
|
|
1995
|
-
u1 = pos[t1]
|
|
1996
|
-
u2 = pos[t2]
|
|
1997
|
-
u3 = pos[t3]
|
|
1998
|
-
|
|
1999
|
-
closest_p, bary, feature_type = triangle_closest_point(u1, u2, u3, v)
|
|
2000
|
-
|
|
2001
|
-
dist = wp.length(closest_p - v)
|
|
2002
|
-
|
|
2003
|
-
if dist < query_radius:
|
|
2004
|
-
# record v-f collision to vertex
|
|
2005
|
-
min_dis_to_tris = wp.min(min_dis_to_tris, dist)
|
|
2006
|
-
if vertex_num_collisions < vertex_buffer_size:
|
|
2007
|
-
vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions)] = v_index
|
|
2008
|
-
vertex_colliding_triangles[2 * (vertex_buffer_offset + vertex_num_collisions) + 1] = tri_index
|
|
2009
|
-
else:
|
|
2010
|
-
resize_flags[VERTEX_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
|
|
2011
|
-
|
|
2012
|
-
vertex_num_collisions = vertex_num_collisions + 1
|
|
2013
|
-
|
|
2014
|
-
wp.atomic_min(triangle_colliding_vertices_min_dist, tri_index, dist)
|
|
2015
|
-
|
|
2016
|
-
vertex_colliding_triangles_count[v_index] = vertex_num_collisions
|
|
2017
|
-
vertex_colliding_triangles_min_dist[v_index] = min_dis_to_tris
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
@wp.kernel
|
|
2021
|
-
def edge_colliding_edges_detection_kernel(
|
|
2022
|
-
query_radius: float,
|
|
2023
|
-
bvh_id: wp.uint64,
|
|
2024
|
-
pos: wp.array(dtype=wp.vec3),
|
|
2025
|
-
edge_indices: wp.array(dtype=wp.int32, ndim=2),
|
|
2026
|
-
edge_colliding_edges_offsets: wp.array(dtype=wp.int32),
|
|
2027
|
-
edge_colliding_edges_buffer_sizes: wp.array(dtype=wp.int32),
|
|
2028
|
-
edge_edge_parallel_epsilon: float,
|
|
2029
|
-
# outputs
|
|
2030
|
-
edge_colliding_edges: wp.array(dtype=wp.int32),
|
|
2031
|
-
edge_colliding_edges_count: wp.array(dtype=wp.int32),
|
|
2032
|
-
edge_colliding_edges_min_dist: wp.array(dtype=float),
|
|
2033
|
-
resize_flags: wp.array(dtype=wp.int32),
|
|
2034
|
-
):
|
|
2035
|
-
"""
|
|
2036
|
-
bvh_id (int): the bvh id you want to do collision detection on
|
|
2037
|
-
query_radius (float):
|
|
2038
|
-
pos (array): positions of all the vertices that make up edges
|
|
2039
|
-
edge_colliding_triangles (array): flattened buffer of edges' collision edges
|
|
2040
|
-
edge_colliding_edges_count (array): number of edges each edge collides
|
|
2041
|
-
edge_colliding_triangles_offsets (array): where each edge's collision buffer starts
|
|
2042
|
-
edge_colliding_triangles_buffer_size (array): size of each edge's collision buffer, will be modified if resizing is needed
|
|
2043
|
-
edge_min_dis_to_triangles (array): each vertex' min distance to all (non-neighbor) triangles
|
|
2044
|
-
resized_flag (array): size == 3, (vertex_buffer_resize_required, triangle_buffer_resize_required, edge_buffer_resize_required)
|
|
2045
|
-
"""
|
|
2046
|
-
e_index = wp.tid()
|
|
2047
|
-
|
|
2048
|
-
e0_v0 = edge_indices[e_index, 2]
|
|
2049
|
-
e0_v1 = edge_indices[e_index, 3]
|
|
2050
|
-
|
|
2051
|
-
e0_v0_pos = pos[e0_v0]
|
|
2052
|
-
e0_v1_pos = pos[e0_v1]
|
|
2053
|
-
|
|
2054
|
-
lower = wp.min(e0_v0_pos, e0_v1_pos)
|
|
2055
|
-
upper = wp.max(e0_v0_pos, e0_v1_pos)
|
|
2056
|
-
|
|
2057
|
-
lower = wp.vec3(lower[0] - query_radius, lower[1] - query_radius, lower[2] - query_radius)
|
|
2058
|
-
upper = wp.vec3(upper[0] + query_radius, upper[1] + query_radius, upper[2] + query_radius)
|
|
2059
|
-
|
|
2060
|
-
query = wp.bvh_query_aabb(bvh_id, lower, upper)
|
|
2061
|
-
|
|
2062
|
-
colliding_edge_index = wp.int32(0)
|
|
2063
|
-
edge_num_collisions = wp.int32(0)
|
|
2064
|
-
min_dis_to_edges = query_radius
|
|
2065
|
-
while wp.bvh_query_next(query, colliding_edge_index):
|
|
2066
|
-
e1_v0 = edge_indices[colliding_edge_index, 2]
|
|
2067
|
-
e1_v1 = edge_indices[colliding_edge_index, 3]
|
|
2068
|
-
|
|
2069
|
-
if e0_v0 == e1_v0 or e0_v0 == e1_v1 or e0_v1 == e1_v0 or e0_v1 == e1_v1:
|
|
2070
|
-
continue
|
|
2071
|
-
|
|
2072
|
-
e1_v0_pos = pos[e1_v0]
|
|
2073
|
-
e1_v1_pos = pos[e1_v1]
|
|
2074
|
-
|
|
2075
|
-
st = wp.closest_point_edge_edge(e0_v0_pos, e0_v1_pos, e1_v0_pos, e1_v1_pos, edge_edge_parallel_epsilon)
|
|
2076
|
-
s = st[0]
|
|
2077
|
-
t = st[1]
|
|
2078
|
-
c1 = e0_v0_pos + (e0_v1_pos - e0_v0_pos) * s
|
|
2079
|
-
c2 = e1_v0_pos + (e1_v1_pos - e1_v0_pos) * t
|
|
2080
|
-
|
|
2081
|
-
dist = wp.length(c1 - c2)
|
|
2082
|
-
if dist < query_radius:
|
|
2083
|
-
edge_buffer_offset = edge_colliding_edges_offsets[e_index]
|
|
2084
|
-
edge_buffer_size = edge_colliding_edges_offsets[e_index + 1] - edge_buffer_offset
|
|
2085
|
-
|
|
2086
|
-
# record e-e collision to e0, and leave e1; e1 will detect this collision from its own thread
|
|
2087
|
-
min_dis_to_edges = wp.min(min_dis_to_edges, dist)
|
|
2088
|
-
if edge_num_collisions < edge_buffer_size:
|
|
2089
|
-
edge_colliding_edges[2 * (edge_buffer_offset + edge_num_collisions)] = e_index
|
|
2090
|
-
edge_colliding_edges[2 * (edge_buffer_offset + edge_num_collisions) + 1] = colliding_edge_index
|
|
2091
|
-
else:
|
|
2092
|
-
resize_flags[EDGE_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
|
|
2093
|
-
|
|
2094
|
-
edge_num_collisions = edge_num_collisions + 1
|
|
2095
|
-
|
|
2096
|
-
edge_colliding_edges_count[e_index] = edge_num_collisions
|
|
2097
|
-
edge_colliding_edges_min_dist[e_index] = min_dis_to_edges
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
@wp.kernel
|
|
2101
|
-
def triangle_triangle_collision_detection_kernel(
|
|
2102
|
-
bvh_id: wp.uint64,
|
|
2103
|
-
pos: wp.array(dtype=wp.vec3),
|
|
2104
|
-
tri_indices: wp.array(dtype=wp.int32, ndim=2),
|
|
2105
|
-
triangle_intersecting_triangles_offsets: wp.array(dtype=wp.int32),
|
|
2106
|
-
# outputs
|
|
2107
|
-
triangle_intersecting_triangles: wp.array(dtype=wp.int32),
|
|
2108
|
-
triangle_intersecting_triangles_count: wp.array(dtype=wp.int32),
|
|
2109
|
-
resize_flags: wp.array(dtype=wp.int32),
|
|
2110
|
-
):
|
|
2111
|
-
tri_index = wp.tid()
|
|
2112
|
-
t1_v1 = tri_indices[tri_index, 0]
|
|
2113
|
-
t1_v2 = tri_indices[tri_index, 1]
|
|
2114
|
-
t1_v3 = tri_indices[tri_index, 2]
|
|
2115
|
-
|
|
2116
|
-
v1 = pos[t1_v1]
|
|
2117
|
-
v2 = pos[t1_v2]
|
|
2118
|
-
v3 = pos[t1_v3]
|
|
2119
|
-
|
|
2120
|
-
lower, upper = compute_tri_aabb(v1, v2, v3)
|
|
2121
|
-
|
|
2122
|
-
buffer_offset = triangle_intersecting_triangles_offsets[tri_index]
|
|
2123
|
-
buffer_size = triangle_intersecting_triangles_offsets[tri_index + 1] - buffer_offset
|
|
2124
|
-
|
|
2125
|
-
query = wp.bvh_query_aabb(bvh_id, lower, upper)
|
|
2126
|
-
tri_index_2 = wp.int32(0)
|
|
2127
|
-
intersection_count = wp.int32(0)
|
|
2128
|
-
while wp.bvh_query_next(query, tri_index_2):
|
|
2129
|
-
t2_v1 = tri_indices[tri_index_2, 0]
|
|
2130
|
-
t2_v2 = tri_indices[tri_index_2, 1]
|
|
2131
|
-
t2_v3 = tri_indices[tri_index_2, 2]
|
|
2132
|
-
|
|
2133
|
-
# filter out intersection test with neighbor triangles
|
|
2134
|
-
if (
|
|
2135
|
-
vertex_adjacent_to_triangle(t1_v1, t2_v1, t2_v2, t2_v3)
|
|
2136
|
-
or vertex_adjacent_to_triangle(t1_v2, t2_v1, t2_v2, t2_v3)
|
|
2137
|
-
or vertex_adjacent_to_triangle(t1_v3, t2_v1, t2_v2, t2_v3)
|
|
2138
|
-
):
|
|
2139
|
-
continue
|
|
2140
|
-
|
|
2141
|
-
u1 = pos[t2_v1]
|
|
2142
|
-
u2 = pos[t2_v2]
|
|
2143
|
-
u3 = pos[t2_v3]
|
|
2144
|
-
|
|
2145
|
-
if wp.intersect_tri_tri(v1, v2, v3, u1, u2, u3):
|
|
2146
|
-
if intersection_count < buffer_size:
|
|
2147
|
-
triangle_intersecting_triangles[buffer_offset + intersection_count] = tri_index_2
|
|
2148
|
-
else:
|
|
2149
|
-
resize_flags[TRI_TRI_COLLISION_BUFFER_OVERFLOW_INDEX] = 1
|
|
2150
|
-
intersection_count = intersection_count + 1
|
|
2151
|
-
|
|
2152
|
-
triangle_intersecting_triangles_count[tri_index] = intersection_count
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
@wp.struct
|
|
2156
|
-
class TriMeshCollisionInfo:
|
|
2157
|
-
vertex_indices: wp.array(dtype=wp.int32)
|
|
2158
|
-
# size: 2 x sum(vertex_colliding_triangles_buffer_sizes)
|
|
2159
|
-
# every two elements records the vertex index and a triangle index it collides to
|
|
2160
|
-
vertex_colliding_triangles: wp.array(dtype=wp.int32)
|
|
2161
|
-
vertex_colliding_triangles_offsets: wp.array(dtype=wp.int32)
|
|
2162
|
-
vertex_colliding_triangles_buffer_sizes: wp.array(dtype=wp.int32)
|
|
2163
|
-
vertex_colliding_triangles_count: wp.array(dtype=wp.int32)
|
|
2164
|
-
vertex_colliding_triangles_min_dist: wp.array(dtype=float)
|
|
2165
|
-
|
|
2166
|
-
triangle_colliding_vertices: wp.array(dtype=wp.int32)
|
|
2167
|
-
triangle_colliding_vertices_offsets: wp.array(dtype=wp.int32)
|
|
2168
|
-
triangle_colliding_vertices_buffer_sizes: wp.array(dtype=wp.int32)
|
|
2169
|
-
triangle_colliding_vertices_count: wp.array(dtype=wp.int32)
|
|
2170
|
-
triangle_colliding_vertices_min_dist: wp.array(dtype=float)
|
|
2171
|
-
|
|
2172
|
-
# size: 2 x sum(edge_colliding_edges_buffer_sizes)
|
|
2173
|
-
# every two elements records the edge index and an edge index it collides to
|
|
2174
|
-
edge_colliding_edges: wp.array(dtype=wp.int32)
|
|
2175
|
-
edge_colliding_edges_offsets: wp.array(dtype=wp.int32)
|
|
2176
|
-
edge_colliding_edges_buffer_sizes: wp.array(dtype=wp.int32)
|
|
2177
|
-
edge_colliding_edges_count: wp.array(dtype=wp.int32)
|
|
2178
|
-
edge_colliding_edges_min_dist: wp.array(dtype=float)
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
@wp.func
|
|
2182
|
-
def get_vertex_colliding_triangles_count(col_info: TriMeshCollisionInfo, v: int):
|
|
2183
|
-
return wp.min(col_info.vertex_colliding_triangles_count[v], col_info.vertex_colliding_triangles_buffer_sizes[v])
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
@wp.func
|
|
2187
|
-
def get_vertex_colliding_triangles(col_info: TriMeshCollisionInfo, v: int, i_collision: int):
|
|
2188
|
-
offset = col_info.vertex_colliding_triangles_offsets[v]
|
|
2189
|
-
return col_info.vertex_colliding_triangles[2 * (offset + i_collision) + 1]
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
@wp.func
|
|
2193
|
-
def get_vertex_collision_buffer_vertex_index(col_info: TriMeshCollisionInfo, v: int, i_collision: int):
|
|
2194
|
-
offset = col_info.vertex_colliding_triangles_offsets[v]
|
|
2195
|
-
return col_info.vertex_colliding_triangles[2 * (offset + i_collision)]
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
@wp.func
|
|
2199
|
-
def get_triangle_colliding_vertices_count(col_info: TriMeshCollisionInfo, tri: int):
|
|
2200
|
-
return wp.min(
|
|
2201
|
-
col_info.triangle_colliding_vertices_count[tri], col_info.triangle_colliding_vertices_buffer_sizes[tri]
|
|
2202
|
-
)
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
@wp.func
|
|
2206
|
-
def get_triangle_colliding_vertices(col_info: TriMeshCollisionInfo, tri: int, i_collision: int):
|
|
2207
|
-
offset = col_info.triangle_colliding_vertices_offsets[tri]
|
|
2208
|
-
return col_info.triangle_colliding_vertices[offset + i_collision]
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
@wp.func
|
|
2212
|
-
def get_edge_colliding_edges_count(col_info: TriMeshCollisionInfo, e: int):
|
|
2213
|
-
return wp.min(col_info.edge_colliding_edges_count[e], col_info.edge_colliding_edges_buffer_sizes[e])
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
@wp.func
|
|
2217
|
-
def get_edge_colliding_edges(col_info: TriMeshCollisionInfo, e: int, i_collision: int):
|
|
2218
|
-
offset = col_info.edge_colliding_edges_offsets[e]
|
|
2219
|
-
return col_info.edge_colliding_edges[2 * (offset + i_collision) + 1]
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
@wp.func
|
|
2223
|
-
def get_edge_collision_buffer_edge_index(col_info: TriMeshCollisionInfo, e: int, i_collision: int):
|
|
2224
|
-
offset = col_info.edge_colliding_edges_offsets[e]
|
|
2225
|
-
return col_info.edge_colliding_edges[2 * (offset + i_collision)]
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
class TriMeshCollisionDetector:
|
|
2229
|
-
def __init__(
|
|
2230
|
-
self,
|
|
2231
|
-
model: Model,
|
|
2232
|
-
record_triangle_contacting_vertices=False,
|
|
2233
|
-
vertex_positions=None,
|
|
2234
|
-
vertex_collision_buffer_pre_alloc=8,
|
|
2235
|
-
vertex_collision_buffer_max_alloc=256,
|
|
2236
|
-
triangle_collision_buffer_pre_alloc=16,
|
|
2237
|
-
triangle_collision_buffer_max_alloc=256,
|
|
2238
|
-
edge_collision_buffer_pre_alloc=8,
|
|
2239
|
-
edge_collision_buffer_max_alloc=256,
|
|
2240
|
-
triangle_triangle_collision_buffer_pre_alloc=8,
|
|
2241
|
-
triangle_triangle_collision_buffer_max_alloc=256,
|
|
2242
|
-
edge_edge_parallel_epsilon=1e-5,
|
|
2243
|
-
):
|
|
2244
|
-
self.model = model
|
|
2245
|
-
self.record_triangle_contacting_vertices = record_triangle_contacting_vertices
|
|
2246
|
-
self.vertex_positions = model.particle_q if vertex_positions is None else vertex_positions
|
|
2247
|
-
self.device = model.device
|
|
2248
|
-
self.vertex_collision_buffer_pre_alloc = vertex_collision_buffer_pre_alloc
|
|
2249
|
-
self.vertex_collision_buffer_max_alloc = vertex_collision_buffer_max_alloc
|
|
2250
|
-
self.triangle_collision_buffer_pre_alloc = triangle_collision_buffer_pre_alloc
|
|
2251
|
-
self.triangle_collision_buffer_max_alloc = triangle_collision_buffer_max_alloc
|
|
2252
|
-
self.edge_collision_buffer_pre_alloc = edge_collision_buffer_pre_alloc
|
|
2253
|
-
self.edge_collision_buffer_max_alloc = edge_collision_buffer_max_alloc
|
|
2254
|
-
self.triangle_triangle_collision_buffer_pre_alloc = triangle_triangle_collision_buffer_pre_alloc
|
|
2255
|
-
self.triangle_triangle_collision_buffer_max_alloc = triangle_triangle_collision_buffer_max_alloc
|
|
2256
|
-
|
|
2257
|
-
self.edge_edge_parallel_epsilon = edge_edge_parallel_epsilon
|
|
2258
|
-
|
|
2259
|
-
self.lower_bounds_tris = wp.array(shape=(model.tri_count,), dtype=wp.vec3, device=model.device)
|
|
2260
|
-
self.upper_bounds_tris = wp.array(shape=(model.tri_count,), dtype=wp.vec3, device=model.device)
|
|
2261
|
-
wp.launch(
|
|
2262
|
-
kernel=compute_tri_aabbs,
|
|
2263
|
-
inputs=[self.vertex_positions, model.tri_indices, self.lower_bounds_tris, self.upper_bounds_tris],
|
|
2264
|
-
dim=model.tri_count,
|
|
2265
|
-
device=model.device,
|
|
2266
|
-
)
|
|
2267
|
-
|
|
2268
|
-
self.bvh_tris = wp.Bvh(self.lower_bounds_tris, self.upper_bounds_tris)
|
|
2269
|
-
|
|
2270
|
-
# collision detections results
|
|
2271
|
-
|
|
2272
|
-
# vertex collision buffers
|
|
2273
|
-
self.vertex_colliding_triangles = wp.zeros(
|
|
2274
|
-
shape=(2 * model.particle_count * self.vertex_collision_buffer_pre_alloc,),
|
|
2275
|
-
dtype=wp.int32,
|
|
2276
|
-
device=self.device,
|
|
2277
|
-
)
|
|
2278
|
-
self.vertex_colliding_triangles_count = wp.array(
|
|
2279
|
-
shape=(model.particle_count,), dtype=wp.int32, device=self.device
|
|
2280
|
-
)
|
|
2281
|
-
self.vertex_colliding_triangles_min_dist = wp.array(
|
|
2282
|
-
shape=(model.particle_count,), dtype=float, device=self.device
|
|
2283
|
-
)
|
|
2284
|
-
self.vertex_colliding_triangles_buffer_sizes = wp.full(
|
|
2285
|
-
shape=(model.particle_count,),
|
|
2286
|
-
value=self.vertex_collision_buffer_pre_alloc,
|
|
2287
|
-
dtype=wp.int32,
|
|
2288
|
-
device=self.device,
|
|
2289
|
-
)
|
|
2290
|
-
self.vertex_colliding_triangles_offsets = wp.array(
|
|
2291
|
-
shape=(model.particle_count + 1,), dtype=wp.int32, device=self.device
|
|
2292
|
-
)
|
|
2293
|
-
self.compute_collision_buffer_offsets(
|
|
2294
|
-
self.vertex_colliding_triangles_buffer_sizes, self.vertex_colliding_triangles_offsets
|
|
2295
|
-
)
|
|
2296
|
-
|
|
2297
|
-
if record_triangle_contacting_vertices:
|
|
2298
|
-
# triangle collision buffers
|
|
2299
|
-
self.triangle_colliding_vertices = wp.zeros(
|
|
2300
|
-
shape=(model.tri_count * self.triangle_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
|
|
2301
|
-
)
|
|
2302
|
-
self.triangle_colliding_vertices_count = wp.zeros(
|
|
2303
|
-
shape=(model.tri_count,), dtype=wp.int32, device=self.device
|
|
2304
|
-
)
|
|
2305
|
-
self.triangle_colliding_vertices_buffer_sizes = wp.full(
|
|
2306
|
-
shape=(model.tri_count,),
|
|
2307
|
-
value=self.triangle_collision_buffer_pre_alloc,
|
|
2308
|
-
dtype=wp.int32,
|
|
2309
|
-
device=self.device,
|
|
2310
|
-
)
|
|
2311
|
-
|
|
2312
|
-
self.triangle_colliding_vertices_offsets = wp.array(
|
|
2313
|
-
shape=(model.tri_count + 1,), dtype=wp.int32, device=self.device
|
|
2314
|
-
)
|
|
2315
|
-
self.compute_collision_buffer_offsets(
|
|
2316
|
-
self.triangle_colliding_vertices_buffer_sizes, self.triangle_colliding_vertices_offsets
|
|
2317
|
-
)
|
|
2318
|
-
else:
|
|
2319
|
-
self.triangle_colliding_vertices = None
|
|
2320
|
-
self.triangle_colliding_vertices_count = None
|
|
2321
|
-
self.triangle_colliding_vertices_buffer_sizes = None
|
|
2322
|
-
self.triangle_colliding_vertices_offsets = None
|
|
2323
|
-
|
|
2324
|
-
# this is need regardless of whether we record triangle contacting vertices
|
|
2325
|
-
self.triangle_colliding_vertices_min_dist = wp.array(shape=(model.tri_count,), dtype=float, device=self.device)
|
|
2326
|
-
|
|
2327
|
-
# edge collision buffers
|
|
2328
|
-
self.edge_colliding_edges = wp.zeros(
|
|
2329
|
-
shape=(2 * model.edge_count * self.edge_collision_buffer_pre_alloc,), dtype=wp.int32, device=self.device
|
|
2330
|
-
)
|
|
2331
|
-
self.edge_colliding_edges_count = wp.zeros(shape=(model.edge_count,), dtype=wp.int32, device=self.device)
|
|
2332
|
-
self.edge_colliding_edges_buffer_sizes = wp.full(
|
|
2333
|
-
shape=(model.edge_count,),
|
|
2334
|
-
value=self.edge_collision_buffer_pre_alloc,
|
|
2335
|
-
dtype=wp.int32,
|
|
2336
|
-
device=self.device,
|
|
2337
|
-
)
|
|
2338
|
-
self.edge_colliding_edges_offsets = wp.array(shape=(model.edge_count + 1,), dtype=wp.int32, device=self.device)
|
|
2339
|
-
self.compute_collision_buffer_offsets(self.edge_colliding_edges_buffer_sizes, self.edge_colliding_edges_offsets)
|
|
2340
|
-
self.edge_colliding_edges_min_dist = wp.array(shape=(model.edge_count,), dtype=float, device=self.device)
|
|
2341
|
-
|
|
2342
|
-
self.lower_bounds_edges = wp.array(shape=(model.edge_count,), dtype=wp.vec3, device=model.device)
|
|
2343
|
-
self.upper_bounds_edges = wp.array(shape=(model.edge_count,), dtype=wp.vec3, device=model.device)
|
|
2344
|
-
wp.launch(
|
|
2345
|
-
kernel=compute_edge_aabbs,
|
|
2346
|
-
inputs=[self.vertex_positions, model.edge_indices, self.lower_bounds_edges, self.upper_bounds_edges],
|
|
2347
|
-
dim=model.edge_count,
|
|
2348
|
-
device=model.device,
|
|
2349
|
-
)
|
|
2350
|
-
|
|
2351
|
-
self.bvh_edges = wp.Bvh(self.lower_bounds_edges, self.upper_bounds_edges)
|
|
2352
|
-
|
|
2353
|
-
self.resize_flags = wp.zeros(shape=(4,), dtype=wp.int32, device=self.device)
|
|
2354
|
-
|
|
2355
|
-
self.collision_info = self.get_collision_data()
|
|
2356
|
-
|
|
2357
|
-
# data for triangle-triangle intersection; they will only be initialized on demand, as triangle-triangle intersection is not needed for simulation
|
|
2358
|
-
self.triangle_intersecting_triangles = None
|
|
2359
|
-
self.triangle_intersecting_triangles_count = None
|
|
2360
|
-
self.triangle_intersecting_triangles_offsets = None
|
|
2361
|
-
|
|
2362
|
-
def get_collision_data(self):
|
|
2363
|
-
collision_info = TriMeshCollisionInfo()
|
|
2364
|
-
|
|
2365
|
-
collision_info.vertex_colliding_triangles = self.vertex_colliding_triangles
|
|
2366
|
-
collision_info.vertex_colliding_triangles_offsets = self.vertex_colliding_triangles_offsets
|
|
2367
|
-
collision_info.vertex_colliding_triangles_buffer_sizes = self.vertex_colliding_triangles_buffer_sizes
|
|
2368
|
-
collision_info.vertex_colliding_triangles_count = self.vertex_colliding_triangles_count
|
|
2369
|
-
collision_info.vertex_colliding_triangles_min_dist = self.vertex_colliding_triangles_min_dist
|
|
2370
|
-
|
|
2371
|
-
if self.record_triangle_contacting_vertices:
|
|
2372
|
-
collision_info.triangle_colliding_vertices = self.triangle_colliding_vertices
|
|
2373
|
-
collision_info.triangle_colliding_vertices_offsets = self.triangle_colliding_vertices_offsets
|
|
2374
|
-
collision_info.triangle_colliding_vertices_buffer_sizes = self.triangle_colliding_vertices_buffer_sizes
|
|
2375
|
-
collision_info.triangle_colliding_vertices_count = self.triangle_colliding_vertices_count
|
|
2376
|
-
|
|
2377
|
-
collision_info.triangle_colliding_vertices_min_dist = self.triangle_colliding_vertices_min_dist
|
|
2378
|
-
|
|
2379
|
-
collision_info.edge_colliding_edges = self.edge_colliding_edges
|
|
2380
|
-
collision_info.edge_colliding_edges_offsets = self.edge_colliding_edges_offsets
|
|
2381
|
-
collision_info.edge_colliding_edges_buffer_sizes = self.edge_colliding_edges_buffer_sizes
|
|
2382
|
-
collision_info.edge_colliding_edges_count = self.edge_colliding_edges_count
|
|
2383
|
-
collision_info.edge_colliding_edges_min_dist = self.edge_colliding_edges_min_dist
|
|
2384
|
-
|
|
2385
|
-
return collision_info
|
|
2386
|
-
|
|
2387
|
-
def compute_collision_buffer_offsets(
|
|
2388
|
-
self, buffer_sizes: wp.array(dtype=wp.int32), offsets: wp.array(dtype=wp.int32)
|
|
2389
|
-
):
|
|
2390
|
-
assert offsets.size == buffer_sizes.size + 1
|
|
2391
|
-
offsets_np = np.empty(shape=(offsets.size,), dtype=np.int32)
|
|
2392
|
-
offsets_np[1:] = np.cumsum(buffer_sizes.numpy())[:]
|
|
2393
|
-
offsets_np[0] = 0
|
|
2394
|
-
|
|
2395
|
-
offsets.assign(offsets_np)
|
|
2396
|
-
|
|
2397
|
-
def rebuild(self, new_pos=None):
|
|
2398
|
-
if new_pos is not None:
|
|
2399
|
-
self.vertex_positions = new_pos
|
|
2400
|
-
|
|
2401
|
-
wp.launch(
|
|
2402
|
-
kernel=compute_tri_aabbs,
|
|
2403
|
-
inputs=[
|
|
2404
|
-
self.vertex_positions,
|
|
2405
|
-
self.model.tri_indices,
|
|
2406
|
-
],
|
|
2407
|
-
outputs=[self.lower_bounds_tris, self.upper_bounds_tris],
|
|
2408
|
-
dim=self.model.tri_count,
|
|
2409
|
-
device=self.model.device,
|
|
2410
|
-
)
|
|
2411
|
-
self.bvh_tris = wp.Bvh(self.lower_bounds_tris, self.upper_bounds_tris)
|
|
2412
|
-
|
|
2413
|
-
wp.launch(
|
|
2414
|
-
kernel=compute_edge_aabbs,
|
|
2415
|
-
inputs=[self.vertex_positions, self.model.edge_indices],
|
|
2416
|
-
outputs=[self.lower_bounds_edges, self.upper_bounds_edges],
|
|
2417
|
-
dim=self.model.edge_count,
|
|
2418
|
-
device=self.model.device,
|
|
2419
|
-
)
|
|
2420
|
-
self.bvh_edges = wp.Bvh(self.lower_bounds_edges, self.upper_bounds_edges)
|
|
2421
|
-
|
|
2422
|
-
def refit(self, new_pos=None):
|
|
2423
|
-
if new_pos is not None:
|
|
2424
|
-
self.vertex_positions = new_pos
|
|
2425
|
-
|
|
2426
|
-
self.refit_triangles()
|
|
2427
|
-
self.refit_edges()
|
|
2428
|
-
|
|
2429
|
-
def refit_triangles(self):
|
|
2430
|
-
wp.launch(
|
|
2431
|
-
kernel=compute_tri_aabbs,
|
|
2432
|
-
inputs=[self.vertex_positions, self.model.tri_indices, self.lower_bounds_tris, self.upper_bounds_tris],
|
|
2433
|
-
dim=self.model.tri_count,
|
|
2434
|
-
device=self.model.device,
|
|
2435
|
-
)
|
|
2436
|
-
self.bvh_tris.refit()
|
|
2437
|
-
|
|
2438
|
-
def refit_edges(self):
|
|
2439
|
-
wp.launch(
|
|
2440
|
-
kernel=compute_edge_aabbs,
|
|
2441
|
-
inputs=[self.vertex_positions, self.model.edge_indices, self.lower_bounds_edges, self.upper_bounds_edges],
|
|
2442
|
-
dim=self.model.edge_count,
|
|
2443
|
-
device=self.model.device,
|
|
2444
|
-
)
|
|
2445
|
-
self.bvh_edges.refit()
|
|
2446
|
-
|
|
2447
|
-
def vertex_triangle_collision_detection(self, query_radius):
|
|
2448
|
-
self.vertex_colliding_triangles.fill_(-1)
|
|
2449
|
-
|
|
2450
|
-
if self.record_triangle_contacting_vertices:
|
|
2451
|
-
wp.launch(
|
|
2452
|
-
kernel=init_triangle_collision_data_kernel,
|
|
2453
|
-
inputs=[
|
|
2454
|
-
query_radius,
|
|
2455
|
-
],
|
|
2456
|
-
outputs=[
|
|
2457
|
-
self.triangle_colliding_vertices_count,
|
|
2458
|
-
self.triangle_colliding_vertices_min_dist,
|
|
2459
|
-
self.resize_flags,
|
|
2460
|
-
],
|
|
2461
|
-
dim=self.model.tri_count,
|
|
2462
|
-
device=self.model.device,
|
|
2463
|
-
)
|
|
2464
|
-
|
|
2465
|
-
wp.launch(
|
|
2466
|
-
kernel=vertex_triangle_collision_detection_kernel,
|
|
2467
|
-
inputs=[
|
|
2468
|
-
query_radius,
|
|
2469
|
-
self.bvh_tris.id,
|
|
2470
|
-
self.vertex_positions,
|
|
2471
|
-
self.model.tri_indices,
|
|
2472
|
-
self.vertex_colliding_triangles_offsets,
|
|
2473
|
-
self.vertex_colliding_triangles_buffer_sizes,
|
|
2474
|
-
self.triangle_colliding_vertices_offsets,
|
|
2475
|
-
self.triangle_colliding_vertices_buffer_sizes,
|
|
2476
|
-
],
|
|
2477
|
-
outputs=[
|
|
2478
|
-
self.vertex_colliding_triangles,
|
|
2479
|
-
self.vertex_colliding_triangles_count,
|
|
2480
|
-
self.vertex_colliding_triangles_min_dist,
|
|
2481
|
-
self.triangle_colliding_vertices,
|
|
2482
|
-
self.triangle_colliding_vertices_count,
|
|
2483
|
-
self.triangle_colliding_vertices_min_dist,
|
|
2484
|
-
self.resize_flags,
|
|
2485
|
-
],
|
|
2486
|
-
dim=self.model.particle_count,
|
|
2487
|
-
device=self.model.device,
|
|
2488
|
-
)
|
|
2489
|
-
else:
|
|
2490
|
-
self.triangle_colliding_vertices_min_dist.fill_(query_radius)
|
|
2491
|
-
wp.launch(
|
|
2492
|
-
kernel=vertex_triangle_collision_detection_no_triangle_buffers_kernel,
|
|
2493
|
-
inputs=[
|
|
2494
|
-
query_radius,
|
|
2495
|
-
self.bvh_tris.id,
|
|
2496
|
-
self.vertex_positions,
|
|
2497
|
-
self.model.tri_indices,
|
|
2498
|
-
self.vertex_colliding_triangles_offsets,
|
|
2499
|
-
self.vertex_colliding_triangles_buffer_sizes,
|
|
2500
|
-
],
|
|
2501
|
-
outputs=[
|
|
2502
|
-
self.vertex_colliding_triangles,
|
|
2503
|
-
self.vertex_colliding_triangles_count,
|
|
2504
|
-
self.vertex_colliding_triangles_min_dist,
|
|
2505
|
-
self.triangle_colliding_vertices_min_dist,
|
|
2506
|
-
self.resize_flags,
|
|
2507
|
-
],
|
|
2508
|
-
dim=self.model.particle_count,
|
|
2509
|
-
device=self.model.device,
|
|
2510
|
-
)
|
|
2511
|
-
|
|
2512
|
-
def edge_edge_collision_detection(self, query_radius):
|
|
2513
|
-
self.edge_colliding_edges.fill_(-1)
|
|
2514
|
-
wp.launch(
|
|
2515
|
-
kernel=edge_colliding_edges_detection_kernel,
|
|
2516
|
-
inputs=[
|
|
2517
|
-
query_radius,
|
|
2518
|
-
self.bvh_edges.id,
|
|
2519
|
-
self.vertex_positions,
|
|
2520
|
-
self.model.edge_indices,
|
|
2521
|
-
self.edge_colliding_edges_offsets,
|
|
2522
|
-
self.edge_colliding_edges_buffer_sizes,
|
|
2523
|
-
self.edge_edge_parallel_epsilon,
|
|
2524
|
-
],
|
|
2525
|
-
outputs=[
|
|
2526
|
-
self.edge_colliding_edges,
|
|
2527
|
-
self.edge_colliding_edges_count,
|
|
2528
|
-
self.edge_colliding_edges_min_dist,
|
|
2529
|
-
self.resize_flags,
|
|
2530
|
-
],
|
|
2531
|
-
dim=self.model.edge_count,
|
|
2532
|
-
device=self.model.device,
|
|
2533
|
-
)
|
|
2534
|
-
|
|
2535
|
-
def triangle_triangle_intersection_detection(self):
|
|
2536
|
-
if self.triangle_intersecting_triangles is None:
|
|
2537
|
-
self.triangle_intersecting_triangles = wp.zeros(
|
|
2538
|
-
shape=(self.model.tri_count * self.triangle_triangle_collision_buffer_pre_alloc,),
|
|
2539
|
-
dtype=wp.int32,
|
|
2540
|
-
device=self.device,
|
|
2541
|
-
)
|
|
2542
|
-
|
|
2543
|
-
if self.triangle_intersecting_triangles_count is None:
|
|
2544
|
-
self.triangle_intersecting_triangles_count = wp.array(
|
|
2545
|
-
shape=(self.model.tri_count,), dtype=wp.int32, device=self.device
|
|
2546
|
-
)
|
|
2547
|
-
|
|
2548
|
-
if self.triangle_intersecting_triangles_offsets is None:
|
|
2549
|
-
buffer_sizes = np.full((self.model.tri_count,), self.triangle_triangle_collision_buffer_pre_alloc)
|
|
2550
|
-
offsets = np.zeros((self.model.tri_count + 1,), dtype=np.int32)
|
|
2551
|
-
offsets[1:] = np.cumsum(buffer_sizes)
|
|
2552
|
-
|
|
2553
|
-
self.triangle_intersecting_triangles_offsets = wp.array(offsets, dtype=wp.int32, device=self.device)
|
|
2554
|
-
|
|
2555
|
-
wp.launch(
|
|
2556
|
-
kernel=triangle_triangle_collision_detection_kernel,
|
|
2557
|
-
inputs=[
|
|
2558
|
-
self.bvh_tris.id,
|
|
2559
|
-
self.vertex_positions,
|
|
2560
|
-
self.model.tri_indices,
|
|
2561
|
-
self.triangle_intersecting_triangles_offsets,
|
|
2562
|
-
],
|
|
2563
|
-
outputs=[
|
|
2564
|
-
self.triangle_intersecting_triangles,
|
|
2565
|
-
self.triangle_intersecting_triangles_count,
|
|
2566
|
-
self.resize_flags,
|
|
2567
|
-
],
|
|
2568
|
-
dim=self.model.tri_count,
|
|
2569
|
-
device=self.model.device,
|
|
2570
|
-
)
|