warp-lang 1.9.1__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.

Files changed (346) hide show
  1. warp/__init__.py +301 -287
  2. warp/__init__.pyi +794 -305
  3. warp/_src/__init__.py +14 -0
  4. warp/_src/autograd.py +1075 -0
  5. warp/_src/build.py +618 -0
  6. warp/_src/build_dll.py +640 -0
  7. warp/{builtins.py → _src/builtins.py} +1382 -377
  8. warp/_src/codegen.py +4359 -0
  9. warp/{config.py → _src/config.py} +178 -169
  10. warp/_src/constants.py +57 -0
  11. warp/_src/context.py +8294 -0
  12. warp/_src/dlpack.py +462 -0
  13. warp/_src/fabric.py +355 -0
  14. warp/_src/fem/__init__.py +14 -0
  15. warp/_src/fem/adaptivity.py +508 -0
  16. warp/_src/fem/cache.py +687 -0
  17. warp/_src/fem/dirichlet.py +188 -0
  18. warp/{fem → _src/fem}/domain.py +40 -30
  19. warp/_src/fem/field/__init__.py +131 -0
  20. warp/_src/fem/field/field.py +701 -0
  21. warp/{fem → _src/fem}/field/nodal_field.py +30 -15
  22. warp/{fem → _src/fem}/field/restriction.py +1 -1
  23. warp/{fem → _src/fem}/field/virtual.py +53 -27
  24. warp/_src/fem/geometry/__init__.py +32 -0
  25. warp/{fem → _src/fem}/geometry/adaptive_nanogrid.py +77 -163
  26. warp/_src/fem/geometry/closest_point.py +97 -0
  27. warp/{fem → _src/fem}/geometry/deformed_geometry.py +14 -22
  28. warp/{fem → _src/fem}/geometry/element.py +32 -10
  29. warp/{fem → _src/fem}/geometry/geometry.py +48 -20
  30. warp/{fem → _src/fem}/geometry/grid_2d.py +12 -23
  31. warp/{fem → _src/fem}/geometry/grid_3d.py +12 -23
  32. warp/{fem → _src/fem}/geometry/hexmesh.py +40 -63
  33. warp/{fem → _src/fem}/geometry/nanogrid.py +255 -248
  34. warp/{fem → _src/fem}/geometry/partition.py +121 -63
  35. warp/{fem → _src/fem}/geometry/quadmesh.py +26 -45
  36. warp/{fem → _src/fem}/geometry/tetmesh.py +40 -63
  37. warp/{fem → _src/fem}/geometry/trimesh.py +26 -45
  38. warp/{fem → _src/fem}/integrate.py +164 -158
  39. warp/_src/fem/linalg.py +383 -0
  40. warp/_src/fem/operator.py +396 -0
  41. warp/_src/fem/polynomial.py +229 -0
  42. warp/{fem → _src/fem}/quadrature/pic_quadrature.py +15 -20
  43. warp/{fem → _src/fem}/quadrature/quadrature.py +95 -47
  44. warp/_src/fem/space/__init__.py +248 -0
  45. warp/{fem → _src/fem}/space/basis_function_space.py +20 -11
  46. warp/_src/fem/space/basis_space.py +679 -0
  47. warp/{fem → _src/fem}/space/dof_mapper.py +3 -3
  48. warp/{fem → _src/fem}/space/function_space.py +14 -13
  49. warp/{fem → _src/fem}/space/grid_2d_function_space.py +4 -7
  50. warp/{fem → _src/fem}/space/grid_3d_function_space.py +4 -4
  51. warp/{fem → _src/fem}/space/hexmesh_function_space.py +4 -10
  52. warp/{fem → _src/fem}/space/nanogrid_function_space.py +3 -9
  53. warp/{fem → _src/fem}/space/partition.py +117 -60
  54. warp/{fem → _src/fem}/space/quadmesh_function_space.py +4 -10
  55. warp/{fem → _src/fem}/space/restriction.py +66 -33
  56. warp/_src/fem/space/shape/__init__.py +152 -0
  57. warp/{fem → _src/fem}/space/shape/cube_shape_function.py +9 -9
  58. warp/{fem → _src/fem}/space/shape/shape_function.py +8 -9
  59. warp/{fem → _src/fem}/space/shape/square_shape_function.py +6 -6
  60. warp/{fem → _src/fem}/space/shape/tet_shape_function.py +3 -3
  61. warp/{fem → _src/fem}/space/shape/triangle_shape_function.py +3 -3
  62. warp/{fem → _src/fem}/space/tetmesh_function_space.py +3 -9
  63. warp/_src/fem/space/topology.py +459 -0
  64. warp/{fem → _src/fem}/space/trimesh_function_space.py +3 -9
  65. warp/_src/fem/types.py +112 -0
  66. warp/_src/fem/utils.py +486 -0
  67. warp/_src/jax.py +186 -0
  68. warp/_src/jax_experimental/__init__.py +14 -0
  69. warp/_src/jax_experimental/custom_call.py +387 -0
  70. warp/_src/jax_experimental/ffi.py +1284 -0
  71. warp/_src/jax_experimental/xla_ffi.py +656 -0
  72. warp/_src/marching_cubes.py +708 -0
  73. warp/_src/math.py +414 -0
  74. warp/_src/optim/__init__.py +14 -0
  75. warp/_src/optim/adam.py +163 -0
  76. warp/_src/optim/linear.py +1606 -0
  77. warp/_src/optim/sgd.py +112 -0
  78. warp/_src/paddle.py +406 -0
  79. warp/_src/render/__init__.py +14 -0
  80. warp/_src/render/imgui_manager.py +289 -0
  81. warp/_src/render/render_opengl.py +3636 -0
  82. warp/_src/render/render_usd.py +937 -0
  83. warp/_src/render/utils.py +160 -0
  84. warp/_src/sparse.py +2716 -0
  85. warp/_src/tape.py +1206 -0
  86. warp/{thirdparty → _src/thirdparty}/unittest_parallel.py +9 -2
  87. warp/_src/torch.py +391 -0
  88. warp/_src/types.py +5870 -0
  89. warp/_src/utils.py +1693 -0
  90. warp/autograd.py +12 -1054
  91. warp/bin/warp-clang.dll +0 -0
  92. warp/bin/warp.dll +0 -0
  93. warp/build.py +8 -588
  94. warp/build_dll.py +6 -721
  95. warp/codegen.py +6 -4251
  96. warp/constants.py +6 -39
  97. warp/context.py +12 -8062
  98. warp/dlpack.py +6 -444
  99. warp/examples/distributed/example_jacobi_mpi.py +4 -5
  100. warp/examples/fem/example_adaptive_grid.py +1 -1
  101. warp/examples/fem/example_apic_fluid.py +1 -1
  102. warp/examples/fem/example_burgers.py +8 -8
  103. warp/examples/fem/example_diffusion.py +1 -1
  104. warp/examples/fem/example_distortion_energy.py +1 -1
  105. warp/examples/fem/example_mixed_elasticity.py +2 -2
  106. warp/examples/fem/example_navier_stokes.py +1 -1
  107. warp/examples/fem/example_nonconforming_contact.py +7 -7
  108. warp/examples/fem/example_stokes.py +1 -1
  109. warp/examples/fem/example_stokes_transfer.py +1 -1
  110. warp/examples/fem/utils.py +2 -2
  111. warp/examples/interop/example_jax_callable.py +1 -1
  112. warp/examples/interop/example_jax_ffi_callback.py +1 -1
  113. warp/examples/interop/example_jax_kernel.py +1 -1
  114. warp/examples/tile/example_tile_mcgp.py +191 -0
  115. warp/fabric.py +6 -337
  116. warp/fem/__init__.py +159 -97
  117. warp/fem/adaptivity.py +7 -489
  118. warp/fem/cache.py +9 -648
  119. warp/fem/dirichlet.py +6 -184
  120. warp/fem/field/__init__.py +8 -109
  121. warp/fem/field/field.py +7 -652
  122. warp/fem/geometry/__init__.py +7 -18
  123. warp/fem/geometry/closest_point.py +11 -77
  124. warp/fem/linalg.py +18 -366
  125. warp/fem/operator.py +11 -369
  126. warp/fem/polynomial.py +9 -209
  127. warp/fem/space/__init__.py +5 -211
  128. warp/fem/space/basis_space.py +6 -662
  129. warp/fem/space/shape/__init__.py +41 -118
  130. warp/fem/space/topology.py +6 -437
  131. warp/fem/types.py +6 -81
  132. warp/fem/utils.py +11 -444
  133. warp/jax.py +8 -165
  134. warp/jax_experimental/__init__.py +14 -1
  135. warp/jax_experimental/custom_call.py +8 -365
  136. warp/jax_experimental/ffi.py +17 -873
  137. warp/jax_experimental/xla_ffi.py +5 -605
  138. warp/marching_cubes.py +5 -689
  139. warp/math.py +16 -393
  140. warp/native/array.h +385 -37
  141. warp/native/builtin.h +314 -37
  142. warp/native/bvh.cpp +43 -9
  143. warp/native/bvh.cu +62 -27
  144. warp/native/bvh.h +310 -309
  145. warp/native/clang/clang.cpp +102 -97
  146. warp/native/coloring.cpp +0 -1
  147. warp/native/crt.h +208 -0
  148. warp/native/exports.h +156 -0
  149. warp/native/hashgrid.cu +2 -0
  150. warp/native/intersect.h +24 -1
  151. warp/native/intersect_tri.h +44 -35
  152. warp/native/mat.h +1456 -276
  153. warp/native/mesh.cpp +4 -4
  154. warp/native/mesh.cu +4 -2
  155. warp/native/mesh.h +176 -61
  156. warp/native/quat.h +0 -52
  157. warp/native/scan.cu +2 -0
  158. warp/native/sparse.cu +7 -3
  159. warp/native/spatial.h +12 -0
  160. warp/native/tile.h +681 -89
  161. warp/native/tile_radix_sort.h +1 -1
  162. warp/native/tile_reduce.h +394 -46
  163. warp/native/tile_scan.h +4 -4
  164. warp/native/vec.h +469 -0
  165. warp/native/version.h +23 -0
  166. warp/native/volume.cpp +1 -1
  167. warp/native/volume.cu +1 -0
  168. warp/native/volume.h +1 -1
  169. warp/native/volume_builder.cu +2 -0
  170. warp/native/warp.cpp +57 -29
  171. warp/native/warp.cu +253 -171
  172. warp/native/warp.h +11 -8
  173. warp/optim/__init__.py +6 -3
  174. warp/optim/adam.py +6 -145
  175. warp/optim/linear.py +14 -1585
  176. warp/optim/sgd.py +6 -94
  177. warp/paddle.py +6 -388
  178. warp/render/__init__.py +8 -4
  179. warp/render/imgui_manager.py +7 -267
  180. warp/render/render_opengl.py +6 -3618
  181. warp/render/render_usd.py +6 -919
  182. warp/render/utils.py +6 -142
  183. warp/sparse.py +37 -2563
  184. warp/tape.py +6 -1188
  185. warp/tests/__main__.py +1 -1
  186. warp/tests/cuda/test_async.py +4 -4
  187. warp/tests/cuda/test_conditional_captures.py +1 -1
  188. warp/tests/cuda/test_multigpu.py +1 -1
  189. warp/tests/cuda/test_streams.py +58 -1
  190. warp/tests/geometry/test_bvh.py +157 -22
  191. warp/tests/geometry/test_marching_cubes.py +0 -1
  192. warp/tests/geometry/test_mesh.py +5 -3
  193. warp/tests/geometry/test_mesh_query_aabb.py +5 -12
  194. warp/tests/geometry/test_mesh_query_point.py +5 -2
  195. warp/tests/geometry/test_mesh_query_ray.py +15 -3
  196. warp/tests/geometry/test_volume_write.py +5 -5
  197. warp/tests/interop/test_dlpack.py +14 -14
  198. warp/tests/interop/test_jax.py +772 -49
  199. warp/tests/interop/test_paddle.py +1 -1
  200. warp/tests/test_adam.py +0 -1
  201. warp/tests/test_arithmetic.py +9 -9
  202. warp/tests/test_array.py +527 -100
  203. warp/tests/test_array_reduce.py +3 -3
  204. warp/tests/test_atomic.py +12 -8
  205. warp/tests/test_atomic_bitwise.py +209 -0
  206. warp/tests/test_atomic_cas.py +4 -4
  207. warp/tests/test_bool.py +2 -2
  208. warp/tests/test_builtins_resolution.py +5 -571
  209. warp/tests/test_codegen.py +33 -14
  210. warp/tests/test_conditional.py +1 -1
  211. warp/tests/test_context.py +6 -6
  212. warp/tests/test_copy.py +242 -161
  213. warp/tests/test_ctypes.py +3 -3
  214. warp/tests/test_devices.py +24 -2
  215. warp/tests/test_examples.py +16 -84
  216. warp/tests/test_fabricarray.py +35 -35
  217. warp/tests/test_fast_math.py +0 -2
  218. warp/tests/test_fem.py +56 -10
  219. warp/tests/test_fixedarray.py +3 -3
  220. warp/tests/test_func.py +8 -5
  221. warp/tests/test_generics.py +1 -1
  222. warp/tests/test_indexedarray.py +24 -24
  223. warp/tests/test_intersect.py +39 -9
  224. warp/tests/test_large.py +1 -1
  225. warp/tests/test_lerp.py +3 -1
  226. warp/tests/test_linear_solvers.py +1 -1
  227. warp/tests/test_map.py +35 -4
  228. warp/tests/test_mat.py +52 -62
  229. warp/tests/test_mat_constructors.py +4 -5
  230. warp/tests/test_mat_lite.py +1 -1
  231. warp/tests/test_mat_scalar_ops.py +121 -121
  232. warp/tests/test_math.py +34 -0
  233. warp/tests/test_module_aot.py +4 -4
  234. warp/tests/test_modules_lite.py +28 -2
  235. warp/tests/test_print.py +11 -11
  236. warp/tests/test_quat.py +93 -58
  237. warp/tests/test_runlength_encode.py +1 -1
  238. warp/tests/test_scalar_ops.py +38 -10
  239. warp/tests/test_smoothstep.py +1 -1
  240. warp/tests/test_sparse.py +126 -15
  241. warp/tests/test_spatial.py +105 -87
  242. warp/tests/test_special_values.py +6 -6
  243. warp/tests/test_static.py +7 -7
  244. warp/tests/test_struct.py +13 -2
  245. warp/tests/test_triangle_closest_point.py +48 -1
  246. warp/tests/test_types.py +27 -15
  247. warp/tests/test_utils.py +52 -52
  248. warp/tests/test_vec.py +29 -29
  249. warp/tests/test_vec_constructors.py +5 -5
  250. warp/tests/test_vec_scalar_ops.py +97 -97
  251. warp/tests/test_version.py +75 -0
  252. warp/tests/tile/test_tile.py +178 -0
  253. warp/tests/tile/test_tile_atomic_bitwise.py +403 -0
  254. warp/tests/tile/test_tile_cholesky.py +7 -4
  255. warp/tests/tile/test_tile_load.py +26 -2
  256. warp/tests/tile/test_tile_mathdx.py +3 -3
  257. warp/tests/tile/test_tile_matmul.py +1 -1
  258. warp/tests/tile/test_tile_mlp.py +2 -4
  259. warp/tests/tile/test_tile_reduce.py +214 -13
  260. warp/tests/unittest_suites.py +6 -14
  261. warp/tests/unittest_utils.py +10 -9
  262. warp/tests/walkthrough_debug.py +3 -1
  263. warp/torch.py +6 -373
  264. warp/types.py +29 -5764
  265. warp/utils.py +10 -1659
  266. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/METADATA +46 -99
  267. warp_lang-1.10.0rc2.dist-info/RECORD +468 -0
  268. warp_lang-1.10.0rc2.dist-info/licenses/licenses/Gaia-LICENSE.txt +6 -0
  269. warp_lang-1.10.0rc2.dist-info/licenses/licenses/appdirs-LICENSE.txt +22 -0
  270. warp_lang-1.10.0rc2.dist-info/licenses/licenses/asset_pixel_jpg-LICENSE.txt +3 -0
  271. warp_lang-1.10.0rc2.dist-info/licenses/licenses/cuda-LICENSE.txt +1582 -0
  272. warp_lang-1.10.0rc2.dist-info/licenses/licenses/dlpack-LICENSE.txt +201 -0
  273. warp_lang-1.10.0rc2.dist-info/licenses/licenses/fp16-LICENSE.txt +28 -0
  274. warp_lang-1.10.0rc2.dist-info/licenses/licenses/libmathdx-LICENSE.txt +220 -0
  275. warp_lang-1.10.0rc2.dist-info/licenses/licenses/llvm-LICENSE.txt +279 -0
  276. warp_lang-1.10.0rc2.dist-info/licenses/licenses/moller-LICENSE.txt +16 -0
  277. warp_lang-1.10.0rc2.dist-info/licenses/licenses/nanovdb-LICENSE.txt +2 -0
  278. warp_lang-1.10.0rc2.dist-info/licenses/licenses/nvrtc-LICENSE.txt +1592 -0
  279. warp_lang-1.10.0rc2.dist-info/licenses/licenses/svd-LICENSE.txt +23 -0
  280. warp_lang-1.10.0rc2.dist-info/licenses/licenses/unittest_parallel-LICENSE.txt +21 -0
  281. warp_lang-1.10.0rc2.dist-info/licenses/licenses/usd-LICENSE.txt +213 -0
  282. warp_lang-1.10.0rc2.dist-info/licenses/licenses/windingnumber-LICENSE.txt +21 -0
  283. warp/examples/assets/cartpole.urdf +0 -110
  284. warp/examples/assets/crazyflie.usd +0 -0
  285. warp/examples/assets/nv_ant.xml +0 -92
  286. warp/examples/assets/nv_humanoid.xml +0 -183
  287. warp/examples/assets/quadruped.urdf +0 -268
  288. warp/examples/optim/example_bounce.py +0 -266
  289. warp/examples/optim/example_cloth_throw.py +0 -228
  290. warp/examples/optim/example_drone.py +0 -870
  291. warp/examples/optim/example_inverse_kinematics.py +0 -182
  292. warp/examples/optim/example_inverse_kinematics_torch.py +0 -191
  293. warp/examples/optim/example_softbody_properties.py +0 -400
  294. warp/examples/optim/example_spring_cage.py +0 -245
  295. warp/examples/optim/example_trajectory.py +0 -227
  296. warp/examples/sim/example_cartpole.py +0 -143
  297. warp/examples/sim/example_cloth.py +0 -225
  298. warp/examples/sim/example_cloth_self_contact.py +0 -316
  299. warp/examples/sim/example_granular.py +0 -130
  300. warp/examples/sim/example_granular_collision_sdf.py +0 -202
  301. warp/examples/sim/example_jacobian_ik.py +0 -244
  302. warp/examples/sim/example_particle_chain.py +0 -124
  303. warp/examples/sim/example_quadruped.py +0 -203
  304. warp/examples/sim/example_rigid_chain.py +0 -203
  305. warp/examples/sim/example_rigid_contact.py +0 -195
  306. warp/examples/sim/example_rigid_force.py +0 -133
  307. warp/examples/sim/example_rigid_gyroscopic.py +0 -115
  308. warp/examples/sim/example_rigid_soft_contact.py +0 -140
  309. warp/examples/sim/example_soft_body.py +0 -196
  310. warp/examples/tile/example_tile_walker.py +0 -327
  311. warp/sim/__init__.py +0 -74
  312. warp/sim/articulation.py +0 -793
  313. warp/sim/collide.py +0 -2570
  314. warp/sim/graph_coloring.py +0 -307
  315. warp/sim/import_mjcf.py +0 -791
  316. warp/sim/import_snu.py +0 -227
  317. warp/sim/import_urdf.py +0 -579
  318. warp/sim/import_usd.py +0 -898
  319. warp/sim/inertia.py +0 -357
  320. warp/sim/integrator.py +0 -245
  321. warp/sim/integrator_euler.py +0 -2000
  322. warp/sim/integrator_featherstone.py +0 -2101
  323. warp/sim/integrator_vbd.py +0 -2487
  324. warp/sim/integrator_xpbd.py +0 -3295
  325. warp/sim/model.py +0 -4821
  326. warp/sim/particles.py +0 -121
  327. warp/sim/render.py +0 -431
  328. warp/sim/utils.py +0 -431
  329. warp/tests/sim/disabled_kinematics.py +0 -244
  330. warp/tests/sim/test_cloth.py +0 -863
  331. warp/tests/sim/test_collision.py +0 -743
  332. warp/tests/sim/test_coloring.py +0 -347
  333. warp/tests/sim/test_inertia.py +0 -161
  334. warp/tests/sim/test_model.py +0 -226
  335. warp/tests/sim/test_sim_grad.py +0 -287
  336. warp/tests/sim/test_sim_grad_bounce_linear.py +0 -212
  337. warp/tests/sim/test_sim_kinematics.py +0 -98
  338. warp/thirdparty/__init__.py +0 -0
  339. warp_lang-1.9.1.dist-info/RECORD +0 -456
  340. /warp/{fem → _src/fem}/quadrature/__init__.py +0 -0
  341. /warp/{tests/sim → _src/thirdparty}/__init__.py +0 -0
  342. /warp/{thirdparty → _src/thirdparty}/appdirs.py +0 -0
  343. /warp/{thirdparty → _src/thirdparty}/dlpack.py +0 -0
  344. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/WHEEL +0 -0
  345. {warp_lang-1.9.1.dist-info → warp_lang-1.10.0rc2.dist-info}/licenses/LICENSE.md +0 -0
  346. {warp_lang-1.9.1.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
- )