warp-lang 1.7.0__py3-none-manylinux_2_34_aarch64.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 (429) hide show
  1. warp/__init__.py +139 -0
  2. warp/__init__.pyi +1 -0
  3. warp/autograd.py +1142 -0
  4. warp/bin/warp-clang.so +0 -0
  5. warp/bin/warp.so +0 -0
  6. warp/build.py +557 -0
  7. warp/build_dll.py +405 -0
  8. warp/builtins.py +6855 -0
  9. warp/codegen.py +3969 -0
  10. warp/config.py +158 -0
  11. warp/constants.py +57 -0
  12. warp/context.py +6812 -0
  13. warp/dlpack.py +462 -0
  14. warp/examples/__init__.py +24 -0
  15. warp/examples/assets/bear.usd +0 -0
  16. warp/examples/assets/bunny.usd +0 -0
  17. warp/examples/assets/cartpole.urdf +110 -0
  18. warp/examples/assets/crazyflie.usd +0 -0
  19. warp/examples/assets/cube.usd +0 -0
  20. warp/examples/assets/nonuniform.usd +0 -0
  21. warp/examples/assets/nv_ant.xml +92 -0
  22. warp/examples/assets/nv_humanoid.xml +183 -0
  23. warp/examples/assets/nvidia_logo.png +0 -0
  24. warp/examples/assets/pixel.jpg +0 -0
  25. warp/examples/assets/quadruped.urdf +268 -0
  26. warp/examples/assets/rocks.nvdb +0 -0
  27. warp/examples/assets/rocks.usd +0 -0
  28. warp/examples/assets/sphere.usd +0 -0
  29. warp/examples/assets/square_cloth.usd +0 -0
  30. warp/examples/benchmarks/benchmark_api.py +389 -0
  31. warp/examples/benchmarks/benchmark_cloth.py +296 -0
  32. warp/examples/benchmarks/benchmark_cloth_cupy.py +96 -0
  33. warp/examples/benchmarks/benchmark_cloth_jax.py +105 -0
  34. warp/examples/benchmarks/benchmark_cloth_numba.py +161 -0
  35. warp/examples/benchmarks/benchmark_cloth_numpy.py +85 -0
  36. warp/examples/benchmarks/benchmark_cloth_paddle.py +94 -0
  37. warp/examples/benchmarks/benchmark_cloth_pytorch.py +94 -0
  38. warp/examples/benchmarks/benchmark_cloth_taichi.py +120 -0
  39. warp/examples/benchmarks/benchmark_cloth_warp.py +153 -0
  40. warp/examples/benchmarks/benchmark_gemm.py +164 -0
  41. warp/examples/benchmarks/benchmark_interop_paddle.py +166 -0
  42. warp/examples/benchmarks/benchmark_interop_torch.py +166 -0
  43. warp/examples/benchmarks/benchmark_launches.py +301 -0
  44. warp/examples/benchmarks/benchmark_tile_load_store.py +103 -0
  45. warp/examples/browse.py +37 -0
  46. warp/examples/core/example_cupy.py +86 -0
  47. warp/examples/core/example_dem.py +241 -0
  48. warp/examples/core/example_fluid.py +299 -0
  49. warp/examples/core/example_graph_capture.py +150 -0
  50. warp/examples/core/example_marching_cubes.py +194 -0
  51. warp/examples/core/example_mesh.py +180 -0
  52. warp/examples/core/example_mesh_intersect.py +211 -0
  53. warp/examples/core/example_nvdb.py +182 -0
  54. warp/examples/core/example_raycast.py +111 -0
  55. warp/examples/core/example_raymarch.py +205 -0
  56. warp/examples/core/example_render_opengl.py +193 -0
  57. warp/examples/core/example_sample_mesh.py +300 -0
  58. warp/examples/core/example_sph.py +411 -0
  59. warp/examples/core/example_torch.py +211 -0
  60. warp/examples/core/example_wave.py +269 -0
  61. warp/examples/fem/example_adaptive_grid.py +286 -0
  62. warp/examples/fem/example_apic_fluid.py +423 -0
  63. warp/examples/fem/example_burgers.py +261 -0
  64. warp/examples/fem/example_convection_diffusion.py +178 -0
  65. warp/examples/fem/example_convection_diffusion_dg.py +204 -0
  66. warp/examples/fem/example_deformed_geometry.py +172 -0
  67. warp/examples/fem/example_diffusion.py +196 -0
  68. warp/examples/fem/example_diffusion_3d.py +225 -0
  69. warp/examples/fem/example_diffusion_mgpu.py +220 -0
  70. warp/examples/fem/example_distortion_energy.py +228 -0
  71. warp/examples/fem/example_magnetostatics.py +240 -0
  72. warp/examples/fem/example_mixed_elasticity.py +291 -0
  73. warp/examples/fem/example_navier_stokes.py +261 -0
  74. warp/examples/fem/example_nonconforming_contact.py +298 -0
  75. warp/examples/fem/example_stokes.py +213 -0
  76. warp/examples/fem/example_stokes_transfer.py +262 -0
  77. warp/examples/fem/example_streamlines.py +352 -0
  78. warp/examples/fem/utils.py +1000 -0
  79. warp/examples/interop/example_jax_callable.py +116 -0
  80. warp/examples/interop/example_jax_ffi_callback.py +132 -0
  81. warp/examples/interop/example_jax_kernel.py +205 -0
  82. warp/examples/optim/example_bounce.py +266 -0
  83. warp/examples/optim/example_cloth_throw.py +228 -0
  84. warp/examples/optim/example_diffray.py +561 -0
  85. warp/examples/optim/example_drone.py +870 -0
  86. warp/examples/optim/example_fluid_checkpoint.py +497 -0
  87. warp/examples/optim/example_inverse_kinematics.py +182 -0
  88. warp/examples/optim/example_inverse_kinematics_torch.py +191 -0
  89. warp/examples/optim/example_softbody_properties.py +400 -0
  90. warp/examples/optim/example_spring_cage.py +245 -0
  91. warp/examples/optim/example_trajectory.py +227 -0
  92. warp/examples/sim/example_cartpole.py +143 -0
  93. warp/examples/sim/example_cloth.py +225 -0
  94. warp/examples/sim/example_cloth_self_contact.py +322 -0
  95. warp/examples/sim/example_granular.py +130 -0
  96. warp/examples/sim/example_granular_collision_sdf.py +202 -0
  97. warp/examples/sim/example_jacobian_ik.py +244 -0
  98. warp/examples/sim/example_particle_chain.py +124 -0
  99. warp/examples/sim/example_quadruped.py +203 -0
  100. warp/examples/sim/example_rigid_chain.py +203 -0
  101. warp/examples/sim/example_rigid_contact.py +195 -0
  102. warp/examples/sim/example_rigid_force.py +133 -0
  103. warp/examples/sim/example_rigid_gyroscopic.py +115 -0
  104. warp/examples/sim/example_rigid_soft_contact.py +140 -0
  105. warp/examples/sim/example_soft_body.py +196 -0
  106. warp/examples/tile/example_tile_cholesky.py +87 -0
  107. warp/examples/tile/example_tile_convolution.py +66 -0
  108. warp/examples/tile/example_tile_fft.py +55 -0
  109. warp/examples/tile/example_tile_filtering.py +113 -0
  110. warp/examples/tile/example_tile_matmul.py +85 -0
  111. warp/examples/tile/example_tile_mlp.py +383 -0
  112. warp/examples/tile/example_tile_nbody.py +199 -0
  113. warp/examples/tile/example_tile_walker.py +327 -0
  114. warp/fabric.py +355 -0
  115. warp/fem/__init__.py +106 -0
  116. warp/fem/adaptivity.py +508 -0
  117. warp/fem/cache.py +572 -0
  118. warp/fem/dirichlet.py +202 -0
  119. warp/fem/domain.py +411 -0
  120. warp/fem/field/__init__.py +125 -0
  121. warp/fem/field/field.py +619 -0
  122. warp/fem/field/nodal_field.py +326 -0
  123. warp/fem/field/restriction.py +37 -0
  124. warp/fem/field/virtual.py +848 -0
  125. warp/fem/geometry/__init__.py +32 -0
  126. warp/fem/geometry/adaptive_nanogrid.py +857 -0
  127. warp/fem/geometry/closest_point.py +84 -0
  128. warp/fem/geometry/deformed_geometry.py +221 -0
  129. warp/fem/geometry/element.py +776 -0
  130. warp/fem/geometry/geometry.py +362 -0
  131. warp/fem/geometry/grid_2d.py +392 -0
  132. warp/fem/geometry/grid_3d.py +452 -0
  133. warp/fem/geometry/hexmesh.py +911 -0
  134. warp/fem/geometry/nanogrid.py +571 -0
  135. warp/fem/geometry/partition.py +389 -0
  136. warp/fem/geometry/quadmesh.py +663 -0
  137. warp/fem/geometry/tetmesh.py +855 -0
  138. warp/fem/geometry/trimesh.py +806 -0
  139. warp/fem/integrate.py +2335 -0
  140. warp/fem/linalg.py +419 -0
  141. warp/fem/operator.py +293 -0
  142. warp/fem/polynomial.py +229 -0
  143. warp/fem/quadrature/__init__.py +17 -0
  144. warp/fem/quadrature/pic_quadrature.py +299 -0
  145. warp/fem/quadrature/quadrature.py +591 -0
  146. warp/fem/space/__init__.py +228 -0
  147. warp/fem/space/basis_function_space.py +468 -0
  148. warp/fem/space/basis_space.py +667 -0
  149. warp/fem/space/dof_mapper.py +251 -0
  150. warp/fem/space/function_space.py +309 -0
  151. warp/fem/space/grid_2d_function_space.py +177 -0
  152. warp/fem/space/grid_3d_function_space.py +227 -0
  153. warp/fem/space/hexmesh_function_space.py +257 -0
  154. warp/fem/space/nanogrid_function_space.py +201 -0
  155. warp/fem/space/partition.py +367 -0
  156. warp/fem/space/quadmesh_function_space.py +223 -0
  157. warp/fem/space/restriction.py +179 -0
  158. warp/fem/space/shape/__init__.py +143 -0
  159. warp/fem/space/shape/cube_shape_function.py +1105 -0
  160. warp/fem/space/shape/shape_function.py +133 -0
  161. warp/fem/space/shape/square_shape_function.py +926 -0
  162. warp/fem/space/shape/tet_shape_function.py +834 -0
  163. warp/fem/space/shape/triangle_shape_function.py +672 -0
  164. warp/fem/space/tetmesh_function_space.py +271 -0
  165. warp/fem/space/topology.py +424 -0
  166. warp/fem/space/trimesh_function_space.py +194 -0
  167. warp/fem/types.py +99 -0
  168. warp/fem/utils.py +420 -0
  169. warp/jax.py +187 -0
  170. warp/jax_experimental/__init__.py +16 -0
  171. warp/jax_experimental/custom_call.py +351 -0
  172. warp/jax_experimental/ffi.py +698 -0
  173. warp/jax_experimental/xla_ffi.py +602 -0
  174. warp/math.py +244 -0
  175. warp/native/array.h +1145 -0
  176. warp/native/builtin.h +1800 -0
  177. warp/native/bvh.cpp +492 -0
  178. warp/native/bvh.cu +791 -0
  179. warp/native/bvh.h +554 -0
  180. warp/native/clang/clang.cpp +536 -0
  181. warp/native/coloring.cpp +613 -0
  182. warp/native/crt.cpp +51 -0
  183. warp/native/crt.h +362 -0
  184. warp/native/cuda_crt.h +1058 -0
  185. warp/native/cuda_util.cpp +646 -0
  186. warp/native/cuda_util.h +307 -0
  187. warp/native/error.cpp +77 -0
  188. warp/native/error.h +36 -0
  189. warp/native/exports.h +1878 -0
  190. warp/native/fabric.h +245 -0
  191. warp/native/hashgrid.cpp +311 -0
  192. warp/native/hashgrid.cu +87 -0
  193. warp/native/hashgrid.h +240 -0
  194. warp/native/initializer_array.h +41 -0
  195. warp/native/intersect.h +1230 -0
  196. warp/native/intersect_adj.h +375 -0
  197. warp/native/intersect_tri.h +339 -0
  198. warp/native/marching.cpp +19 -0
  199. warp/native/marching.cu +514 -0
  200. warp/native/marching.h +19 -0
  201. warp/native/mat.h +2220 -0
  202. warp/native/mathdx.cpp +87 -0
  203. warp/native/matnn.h +343 -0
  204. warp/native/mesh.cpp +266 -0
  205. warp/native/mesh.cu +404 -0
  206. warp/native/mesh.h +1980 -0
  207. warp/native/nanovdb/GridHandle.h +366 -0
  208. warp/native/nanovdb/HostBuffer.h +590 -0
  209. warp/native/nanovdb/NanoVDB.h +6624 -0
  210. warp/native/nanovdb/PNanoVDB.h +3390 -0
  211. warp/native/noise.h +859 -0
  212. warp/native/quat.h +1371 -0
  213. warp/native/rand.h +342 -0
  214. warp/native/range.h +139 -0
  215. warp/native/reduce.cpp +174 -0
  216. warp/native/reduce.cu +364 -0
  217. warp/native/runlength_encode.cpp +79 -0
  218. warp/native/runlength_encode.cu +61 -0
  219. warp/native/scan.cpp +47 -0
  220. warp/native/scan.cu +53 -0
  221. warp/native/scan.h +23 -0
  222. warp/native/solid_angle.h +466 -0
  223. warp/native/sort.cpp +251 -0
  224. warp/native/sort.cu +277 -0
  225. warp/native/sort.h +33 -0
  226. warp/native/sparse.cpp +378 -0
  227. warp/native/sparse.cu +524 -0
  228. warp/native/spatial.h +657 -0
  229. warp/native/svd.h +702 -0
  230. warp/native/temp_buffer.h +46 -0
  231. warp/native/tile.h +2584 -0
  232. warp/native/tile_reduce.h +264 -0
  233. warp/native/vec.h +1426 -0
  234. warp/native/volume.cpp +501 -0
  235. warp/native/volume.cu +67 -0
  236. warp/native/volume.h +969 -0
  237. warp/native/volume_builder.cu +477 -0
  238. warp/native/volume_builder.h +52 -0
  239. warp/native/volume_impl.h +70 -0
  240. warp/native/warp.cpp +1082 -0
  241. warp/native/warp.cu +3636 -0
  242. warp/native/warp.h +381 -0
  243. warp/optim/__init__.py +17 -0
  244. warp/optim/adam.py +163 -0
  245. warp/optim/linear.py +1137 -0
  246. warp/optim/sgd.py +112 -0
  247. warp/paddle.py +407 -0
  248. warp/render/__init__.py +18 -0
  249. warp/render/render_opengl.py +3518 -0
  250. warp/render/render_usd.py +784 -0
  251. warp/render/utils.py +160 -0
  252. warp/sim/__init__.py +65 -0
  253. warp/sim/articulation.py +793 -0
  254. warp/sim/collide.py +2395 -0
  255. warp/sim/graph_coloring.py +300 -0
  256. warp/sim/import_mjcf.py +790 -0
  257. warp/sim/import_snu.py +227 -0
  258. warp/sim/import_urdf.py +579 -0
  259. warp/sim/import_usd.py +894 -0
  260. warp/sim/inertia.py +324 -0
  261. warp/sim/integrator.py +242 -0
  262. warp/sim/integrator_euler.py +1997 -0
  263. warp/sim/integrator_featherstone.py +2101 -0
  264. warp/sim/integrator_vbd.py +2048 -0
  265. warp/sim/integrator_xpbd.py +3292 -0
  266. warp/sim/model.py +4791 -0
  267. warp/sim/particles.py +121 -0
  268. warp/sim/render.py +427 -0
  269. warp/sim/utils.py +428 -0
  270. warp/sparse.py +2057 -0
  271. warp/stubs.py +3333 -0
  272. warp/tape.py +1203 -0
  273. warp/tests/__init__.py +1 -0
  274. warp/tests/__main__.py +4 -0
  275. warp/tests/assets/curlnoise_golden.npy +0 -0
  276. warp/tests/assets/mlp_golden.npy +0 -0
  277. warp/tests/assets/pixel.npy +0 -0
  278. warp/tests/assets/pnoise_golden.npy +0 -0
  279. warp/tests/assets/spiky.usd +0 -0
  280. warp/tests/assets/test_grid.nvdb +0 -0
  281. warp/tests/assets/test_index_grid.nvdb +0 -0
  282. warp/tests/assets/test_int32_grid.nvdb +0 -0
  283. warp/tests/assets/test_vec_grid.nvdb +0 -0
  284. warp/tests/assets/torus.nvdb +0 -0
  285. warp/tests/assets/torus.usda +105 -0
  286. warp/tests/aux_test_class_kernel.py +34 -0
  287. warp/tests/aux_test_compile_consts_dummy.py +18 -0
  288. warp/tests/aux_test_conditional_unequal_types_kernels.py +29 -0
  289. warp/tests/aux_test_dependent.py +29 -0
  290. warp/tests/aux_test_grad_customs.py +29 -0
  291. warp/tests/aux_test_instancing_gc.py +26 -0
  292. warp/tests/aux_test_module_unload.py +23 -0
  293. warp/tests/aux_test_name_clash1.py +40 -0
  294. warp/tests/aux_test_name_clash2.py +40 -0
  295. warp/tests/aux_test_reference.py +9 -0
  296. warp/tests/aux_test_reference_reference.py +8 -0
  297. warp/tests/aux_test_square.py +16 -0
  298. warp/tests/aux_test_unresolved_func.py +22 -0
  299. warp/tests/aux_test_unresolved_symbol.py +22 -0
  300. warp/tests/cuda/__init__.py +0 -0
  301. warp/tests/cuda/test_async.py +676 -0
  302. warp/tests/cuda/test_ipc.py +124 -0
  303. warp/tests/cuda/test_mempool.py +233 -0
  304. warp/tests/cuda/test_multigpu.py +169 -0
  305. warp/tests/cuda/test_peer.py +139 -0
  306. warp/tests/cuda/test_pinned.py +84 -0
  307. warp/tests/cuda/test_streams.py +634 -0
  308. warp/tests/geometry/__init__.py +0 -0
  309. warp/tests/geometry/test_bvh.py +200 -0
  310. warp/tests/geometry/test_hash_grid.py +221 -0
  311. warp/tests/geometry/test_marching_cubes.py +74 -0
  312. warp/tests/geometry/test_mesh.py +316 -0
  313. warp/tests/geometry/test_mesh_query_aabb.py +399 -0
  314. warp/tests/geometry/test_mesh_query_point.py +932 -0
  315. warp/tests/geometry/test_mesh_query_ray.py +311 -0
  316. warp/tests/geometry/test_volume.py +1103 -0
  317. warp/tests/geometry/test_volume_write.py +346 -0
  318. warp/tests/interop/__init__.py +0 -0
  319. warp/tests/interop/test_dlpack.py +729 -0
  320. warp/tests/interop/test_jax.py +371 -0
  321. warp/tests/interop/test_paddle.py +800 -0
  322. warp/tests/interop/test_torch.py +1001 -0
  323. warp/tests/run_coverage_serial.py +39 -0
  324. warp/tests/sim/__init__.py +0 -0
  325. warp/tests/sim/disabled_kinematics.py +244 -0
  326. warp/tests/sim/flaky_test_sim_grad.py +290 -0
  327. warp/tests/sim/test_collision.py +604 -0
  328. warp/tests/sim/test_coloring.py +258 -0
  329. warp/tests/sim/test_model.py +224 -0
  330. warp/tests/sim/test_sim_grad_bounce_linear.py +212 -0
  331. warp/tests/sim/test_sim_kinematics.py +98 -0
  332. warp/tests/sim/test_vbd.py +597 -0
  333. warp/tests/test_adam.py +163 -0
  334. warp/tests/test_arithmetic.py +1096 -0
  335. warp/tests/test_array.py +2972 -0
  336. warp/tests/test_array_reduce.py +156 -0
  337. warp/tests/test_assert.py +250 -0
  338. warp/tests/test_atomic.py +153 -0
  339. warp/tests/test_bool.py +220 -0
  340. warp/tests/test_builtins_resolution.py +1298 -0
  341. warp/tests/test_closest_point_edge_edge.py +327 -0
  342. warp/tests/test_codegen.py +810 -0
  343. warp/tests/test_codegen_instancing.py +1495 -0
  344. warp/tests/test_compile_consts.py +215 -0
  345. warp/tests/test_conditional.py +252 -0
  346. warp/tests/test_context.py +42 -0
  347. warp/tests/test_copy.py +238 -0
  348. warp/tests/test_ctypes.py +638 -0
  349. warp/tests/test_dense.py +73 -0
  350. warp/tests/test_devices.py +97 -0
  351. warp/tests/test_examples.py +482 -0
  352. warp/tests/test_fabricarray.py +996 -0
  353. warp/tests/test_fast_math.py +74 -0
  354. warp/tests/test_fem.py +2003 -0
  355. warp/tests/test_fp16.py +136 -0
  356. warp/tests/test_func.py +454 -0
  357. warp/tests/test_future_annotations.py +98 -0
  358. warp/tests/test_generics.py +656 -0
  359. warp/tests/test_grad.py +893 -0
  360. warp/tests/test_grad_customs.py +339 -0
  361. warp/tests/test_grad_debug.py +341 -0
  362. warp/tests/test_implicit_init.py +411 -0
  363. warp/tests/test_import.py +45 -0
  364. warp/tests/test_indexedarray.py +1140 -0
  365. warp/tests/test_intersect.py +73 -0
  366. warp/tests/test_iter.py +76 -0
  367. warp/tests/test_large.py +177 -0
  368. warp/tests/test_launch.py +411 -0
  369. warp/tests/test_lerp.py +151 -0
  370. warp/tests/test_linear_solvers.py +193 -0
  371. warp/tests/test_lvalue.py +427 -0
  372. warp/tests/test_mat.py +2089 -0
  373. warp/tests/test_mat_lite.py +122 -0
  374. warp/tests/test_mat_scalar_ops.py +2913 -0
  375. warp/tests/test_math.py +178 -0
  376. warp/tests/test_mlp.py +282 -0
  377. warp/tests/test_module_hashing.py +258 -0
  378. warp/tests/test_modules_lite.py +44 -0
  379. warp/tests/test_noise.py +252 -0
  380. warp/tests/test_operators.py +299 -0
  381. warp/tests/test_options.py +129 -0
  382. warp/tests/test_overwrite.py +551 -0
  383. warp/tests/test_print.py +339 -0
  384. warp/tests/test_quat.py +2315 -0
  385. warp/tests/test_rand.py +339 -0
  386. warp/tests/test_reload.py +302 -0
  387. warp/tests/test_rounding.py +185 -0
  388. warp/tests/test_runlength_encode.py +196 -0
  389. warp/tests/test_scalar_ops.py +105 -0
  390. warp/tests/test_smoothstep.py +108 -0
  391. warp/tests/test_snippet.py +318 -0
  392. warp/tests/test_sparse.py +582 -0
  393. warp/tests/test_spatial.py +2229 -0
  394. warp/tests/test_special_values.py +361 -0
  395. warp/tests/test_static.py +592 -0
  396. warp/tests/test_struct.py +734 -0
  397. warp/tests/test_tape.py +204 -0
  398. warp/tests/test_transient_module.py +93 -0
  399. warp/tests/test_triangle_closest_point.py +145 -0
  400. warp/tests/test_types.py +562 -0
  401. warp/tests/test_utils.py +588 -0
  402. warp/tests/test_vec.py +1487 -0
  403. warp/tests/test_vec_lite.py +80 -0
  404. warp/tests/test_vec_scalar_ops.py +2327 -0
  405. warp/tests/test_verify_fp.py +100 -0
  406. warp/tests/tile/__init__.py +0 -0
  407. warp/tests/tile/test_tile.py +780 -0
  408. warp/tests/tile/test_tile_load.py +407 -0
  409. warp/tests/tile/test_tile_mathdx.py +208 -0
  410. warp/tests/tile/test_tile_mlp.py +402 -0
  411. warp/tests/tile/test_tile_reduce.py +447 -0
  412. warp/tests/tile/test_tile_shared_memory.py +247 -0
  413. warp/tests/tile/test_tile_view.py +173 -0
  414. warp/tests/unittest_serial.py +47 -0
  415. warp/tests/unittest_suites.py +427 -0
  416. warp/tests/unittest_utils.py +468 -0
  417. warp/tests/walkthrough_debug.py +93 -0
  418. warp/thirdparty/__init__.py +0 -0
  419. warp/thirdparty/appdirs.py +598 -0
  420. warp/thirdparty/dlpack.py +145 -0
  421. warp/thirdparty/unittest_parallel.py +570 -0
  422. warp/torch.py +391 -0
  423. warp/types.py +5230 -0
  424. warp/utils.py +1137 -0
  425. warp_lang-1.7.0.dist-info/METADATA +516 -0
  426. warp_lang-1.7.0.dist-info/RECORD +429 -0
  427. warp_lang-1.7.0.dist-info/WHEEL +5 -0
  428. warp_lang-1.7.0.dist-info/licenses/LICENSE.md +202 -0
  429. warp_lang-1.7.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3292 @@
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
+ import warp as wp
17
+
18
+ from .integrator import Integrator
19
+ from .model import (
20
+ JOINT_MODE_FORCE,
21
+ JOINT_MODE_TARGET_POSITION,
22
+ JOINT_MODE_TARGET_VELOCITY,
23
+ PARTICLE_FLAG_ACTIVE,
24
+ Control,
25
+ Model,
26
+ ModelShapeMaterials,
27
+ State,
28
+ )
29
+ from .utils import vec_abs, vec_leaky_max, vec_leaky_min, vec_max, vec_min, velocity_at_point
30
+
31
+
32
+ @wp.kernel
33
+ def solve_particle_ground_contacts(
34
+ particle_x: wp.array(dtype=wp.vec3),
35
+ particle_v: wp.array(dtype=wp.vec3),
36
+ invmass: wp.array(dtype=float),
37
+ particle_radius: wp.array(dtype=float),
38
+ particle_flags: wp.array(dtype=wp.uint32),
39
+ ke: float,
40
+ kd: float,
41
+ kf: float,
42
+ mu: float,
43
+ ground: wp.array(dtype=float),
44
+ dt: float,
45
+ relaxation: float,
46
+ delta: wp.array(dtype=wp.vec3),
47
+ ):
48
+ tid = wp.tid()
49
+ if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
50
+ return
51
+
52
+ wi = invmass[tid]
53
+ if wi == 0.0:
54
+ return
55
+
56
+ x = particle_x[tid]
57
+ v = particle_v[tid]
58
+
59
+ n = wp.vec3(ground[0], ground[1], ground[2])
60
+ c = wp.min(wp.dot(n, x) + ground[3] - particle_radius[tid], 0.0)
61
+
62
+ if c > 0.0:
63
+ return
64
+
65
+ # normal
66
+ lambda_n = c
67
+ delta_n = n * lambda_n
68
+
69
+ # friction
70
+ vn = wp.dot(n, v)
71
+ vt = v - n * vn
72
+
73
+ lambda_f = wp.max(mu * lambda_n, 0.0 - wp.length(vt) * dt)
74
+ delta_f = wp.normalize(vt) * lambda_f
75
+
76
+ wp.atomic_add(delta, tid, (delta_f - delta_n) * relaxation)
77
+
78
+
79
+ @wp.kernel
80
+ def apply_particle_shape_restitution(
81
+ particle_x_new: wp.array(dtype=wp.vec3),
82
+ particle_v_new: wp.array(dtype=wp.vec3),
83
+ particle_x_old: wp.array(dtype=wp.vec3),
84
+ particle_v_old: wp.array(dtype=wp.vec3),
85
+ particle_invmass: wp.array(dtype=float),
86
+ particle_radius: wp.array(dtype=float),
87
+ particle_flags: wp.array(dtype=wp.uint32),
88
+ body_q: wp.array(dtype=wp.transform),
89
+ body_qd: wp.array(dtype=wp.spatial_vector),
90
+ body_com: wp.array(dtype=wp.vec3),
91
+ body_m_inv: wp.array(dtype=float),
92
+ body_I_inv: wp.array(dtype=wp.mat33),
93
+ shape_body: wp.array(dtype=int),
94
+ shape_materials: ModelShapeMaterials,
95
+ particle_ka: float,
96
+ restitution: float,
97
+ contact_count: wp.array(dtype=int),
98
+ contact_particle: wp.array(dtype=int),
99
+ contact_shape: wp.array(dtype=int),
100
+ contact_body_pos: wp.array(dtype=wp.vec3),
101
+ contact_body_vel: wp.array(dtype=wp.vec3),
102
+ contact_normal: wp.array(dtype=wp.vec3),
103
+ contact_max: int,
104
+ dt: float,
105
+ relaxation: float,
106
+ particle_v_out: wp.array(dtype=wp.vec3),
107
+ ):
108
+ tid = wp.tid()
109
+
110
+ count = min(contact_max, contact_count[0])
111
+ if tid >= count:
112
+ return
113
+
114
+ shape_index = contact_shape[tid]
115
+ body_index = shape_body[shape_index]
116
+ particle_index = contact_particle[tid]
117
+
118
+ if (particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE) == 0:
119
+ return
120
+
121
+ # x_new = particle_x_new[particle_index]
122
+ v_new = particle_v_new[particle_index]
123
+ px = particle_x_old[particle_index]
124
+ v_old = particle_v_old[particle_index]
125
+
126
+ X_wb = wp.transform_identity()
127
+ # X_com = wp.vec3()
128
+
129
+ if body_index >= 0:
130
+ X_wb = body_q[body_index]
131
+ # X_com = body_com[body_index]
132
+
133
+ # body position in world space
134
+ bx = wp.transform_point(X_wb, contact_body_pos[tid])
135
+ # r = bx - wp.transform_point(X_wb, X_com)
136
+
137
+ n = contact_normal[tid]
138
+ c = wp.dot(n, px - bx) - particle_radius[particle_index]
139
+
140
+ if c > particle_ka:
141
+ return
142
+
143
+ rel_vel_old = wp.dot(n, v_old)
144
+ rel_vel_new = wp.dot(n, v_new)
145
+
146
+ if rel_vel_old < 0.0:
147
+ # dv = -n * wp.max(-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0), 0.0)
148
+ dv = n * (-rel_vel_new + wp.max(-restitution * rel_vel_old, 0.0))
149
+
150
+ # compute inverse masses
151
+ # w1 = particle_invmass[particle_index]
152
+ # w2 = 0.0
153
+ # if body_index >= 0:
154
+ # angular = wp.cross(r, n)
155
+ # q = wp.transform_get_rotation(X_wb)
156
+ # rot_angular = wp.quat_rotate_inv(q, angular)
157
+ # I_inv = body_I_inv[body_index]
158
+ # w2 = body_m_inv[body_index] + wp.dot(rot_angular, I_inv * rot_angular)
159
+ # denom = w1 + w2
160
+ # if denom == 0.0:
161
+ # return
162
+
163
+ wp.atomic_add(particle_v_out, tid, dv)
164
+
165
+
166
+ @wp.kernel
167
+ def apply_particle_ground_restitution(
168
+ particle_x_new: wp.array(dtype=wp.vec3),
169
+ particle_v_new: wp.array(dtype=wp.vec3),
170
+ particle_x_old: wp.array(dtype=wp.vec3),
171
+ particle_v_old: wp.array(dtype=wp.vec3),
172
+ particle_invmass: wp.array(dtype=float),
173
+ particle_radius: wp.array(dtype=float),
174
+ particle_flags: wp.array(dtype=wp.uint32),
175
+ particle_ka: float,
176
+ restitution: float,
177
+ ground: wp.array(dtype=float),
178
+ dt: float,
179
+ relaxation: float,
180
+ particle_v_out: wp.array(dtype=wp.vec3),
181
+ ):
182
+ tid = wp.tid()
183
+ if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
184
+ return
185
+
186
+ wi = particle_invmass[tid]
187
+ if wi == 0.0:
188
+ return
189
+
190
+ x = particle_x_old[tid]
191
+ v_old = particle_v_old[tid]
192
+ v_new = particle_v_new[tid]
193
+
194
+ n = wp.vec3(ground[0], ground[1], ground[2])
195
+ c = wp.dot(n, x) + ground[3] - particle_radius[tid]
196
+
197
+ if c > particle_ka:
198
+ return
199
+
200
+ vn = wp.dot(n, v_old)
201
+ vn_new = wp.dot(n, v_new)
202
+
203
+ if vn < 0.0:
204
+ dv = n * (-vn_new + wp.max(-restitution * vn, 0.0))
205
+ wp.atomic_add(particle_v_out, tid, dv)
206
+
207
+
208
+ @wp.kernel
209
+ def solve_particle_shape_contacts(
210
+ particle_x: wp.array(dtype=wp.vec3),
211
+ particle_v: wp.array(dtype=wp.vec3),
212
+ particle_invmass: wp.array(dtype=float),
213
+ particle_radius: wp.array(dtype=float),
214
+ particle_flags: wp.array(dtype=wp.uint32),
215
+ body_q: wp.array(dtype=wp.transform),
216
+ body_qd: wp.array(dtype=wp.spatial_vector),
217
+ body_com: wp.array(dtype=wp.vec3),
218
+ body_m_inv: wp.array(dtype=float),
219
+ body_I_inv: wp.array(dtype=wp.mat33),
220
+ shape_body: wp.array(dtype=int),
221
+ shape_materials: ModelShapeMaterials,
222
+ particle_mu: float,
223
+ particle_ka: float,
224
+ contact_count: wp.array(dtype=int),
225
+ contact_particle: wp.array(dtype=int),
226
+ contact_shape: wp.array(dtype=int),
227
+ contact_body_pos: wp.array(dtype=wp.vec3),
228
+ contact_body_vel: wp.array(dtype=wp.vec3),
229
+ contact_normal: wp.array(dtype=wp.vec3),
230
+ contact_max: int,
231
+ dt: float,
232
+ relaxation: float,
233
+ # outputs
234
+ delta: wp.array(dtype=wp.vec3),
235
+ body_delta: wp.array(dtype=wp.spatial_vector),
236
+ ):
237
+ tid = wp.tid()
238
+
239
+ count = min(contact_max, contact_count[0])
240
+ if tid >= count:
241
+ return
242
+
243
+ shape_index = contact_shape[tid]
244
+ body_index = shape_body[shape_index]
245
+ particle_index = contact_particle[tid]
246
+
247
+ if (particle_flags[particle_index] & PARTICLE_FLAG_ACTIVE) == 0:
248
+ return
249
+
250
+ px = particle_x[particle_index]
251
+ pv = particle_v[particle_index]
252
+
253
+ X_wb = wp.transform_identity()
254
+ X_com = wp.vec3()
255
+
256
+ if body_index >= 0:
257
+ X_wb = body_q[body_index]
258
+ X_com = body_com[body_index]
259
+
260
+ # body position in world space
261
+ bx = wp.transform_point(X_wb, contact_body_pos[tid])
262
+ r = bx - wp.transform_point(X_wb, X_com)
263
+
264
+ n = contact_normal[tid]
265
+ c = wp.dot(n, px - bx) - particle_radius[particle_index]
266
+
267
+ if c > particle_ka:
268
+ return
269
+
270
+ # take average material properties of shape and particle parameters
271
+ mu = 0.5 * (particle_mu + shape_materials.mu[shape_index])
272
+
273
+ # body velocity
274
+ body_v_s = wp.spatial_vector()
275
+ if body_index >= 0:
276
+ body_v_s = body_qd[body_index]
277
+
278
+ body_w = wp.spatial_top(body_v_s)
279
+ body_v = wp.spatial_bottom(body_v_s)
280
+
281
+ # compute the body velocity at the particle position
282
+ bv = body_v + wp.cross(body_w, r) + wp.transform_vector(X_wb, contact_body_vel[tid])
283
+
284
+ # relative velocity
285
+ v = pv - bv
286
+
287
+ # normal
288
+ lambda_n = c
289
+ delta_n = n * lambda_n
290
+
291
+ # friction
292
+ vn = wp.dot(n, v)
293
+ vt = v - n * vn
294
+
295
+ # compute inverse masses
296
+ w1 = particle_invmass[particle_index]
297
+ w2 = 0.0
298
+ if body_index >= 0:
299
+ angular = wp.cross(r, n)
300
+ q = wp.transform_get_rotation(X_wb)
301
+ rot_angular = wp.quat_rotate_inv(q, angular)
302
+ I_inv = body_I_inv[body_index]
303
+ w2 = body_m_inv[body_index] + wp.dot(rot_angular, I_inv * rot_angular)
304
+ denom = w1 + w2
305
+ if denom == 0.0:
306
+ return
307
+
308
+ lambda_f = wp.max(mu * lambda_n, -wp.length(vt) * dt)
309
+ delta_f = wp.normalize(vt) * lambda_f
310
+ delta_total = (delta_f - delta_n) / denom * relaxation
311
+
312
+ wp.atomic_add(delta, particle_index, w1 * delta_total)
313
+
314
+ if body_index >= 0:
315
+ delta_t = wp.cross(r, delta_total)
316
+ wp.atomic_sub(body_delta, body_index, wp.spatial_vector(delta_t, delta_total))
317
+
318
+
319
+ @wp.kernel
320
+ def solve_particle_particle_contacts(
321
+ grid: wp.uint64,
322
+ particle_x: wp.array(dtype=wp.vec3),
323
+ particle_v: wp.array(dtype=wp.vec3),
324
+ particle_invmass: wp.array(dtype=float),
325
+ particle_radius: wp.array(dtype=float),
326
+ particle_flags: wp.array(dtype=wp.uint32),
327
+ k_mu: float,
328
+ k_cohesion: float,
329
+ max_radius: float,
330
+ dt: float,
331
+ relaxation: float,
332
+ # outputs
333
+ deltas: wp.array(dtype=wp.vec3),
334
+ ):
335
+ tid = wp.tid()
336
+
337
+ # order threads by cell
338
+ i = wp.hash_grid_point_id(grid, tid)
339
+ if i == -1:
340
+ # hash grid has not been built yet
341
+ return
342
+ if (particle_flags[i] & PARTICLE_FLAG_ACTIVE) == 0:
343
+ return
344
+
345
+ x = particle_x[i]
346
+ v = particle_v[i]
347
+ radius = particle_radius[i]
348
+ w1 = particle_invmass[i]
349
+
350
+ # particle contact
351
+ query = wp.hash_grid_query(grid, x, radius + max_radius + k_cohesion)
352
+ index = int(0)
353
+
354
+ delta = wp.vec3(0.0)
355
+
356
+ while wp.hash_grid_query_next(query, index):
357
+ if (particle_flags[index] & PARTICLE_FLAG_ACTIVE) != 0 and index != i:
358
+ # compute distance to point
359
+ n = x - particle_x[index]
360
+ d = wp.length(n)
361
+ err = d - radius - particle_radius[index]
362
+
363
+ # compute inverse masses
364
+ w2 = particle_invmass[index]
365
+ denom = w1 + w2
366
+
367
+ if err <= k_cohesion and denom > 0.0:
368
+ n = n / d
369
+ vrel = v - particle_v[index]
370
+
371
+ # normal
372
+ lambda_n = err
373
+ delta_n = n * lambda_n
374
+
375
+ # friction
376
+ vn = wp.dot(n, vrel)
377
+ vt = v - n * vn
378
+
379
+ lambda_f = wp.max(k_mu * lambda_n, -wp.length(vt) * dt)
380
+ delta_f = wp.normalize(vt) * lambda_f
381
+ delta += (delta_f - delta_n) / denom
382
+
383
+ wp.atomic_add(deltas, i, delta * w1 * relaxation)
384
+
385
+
386
+ @wp.kernel
387
+ def solve_springs(
388
+ x: wp.array(dtype=wp.vec3),
389
+ v: wp.array(dtype=wp.vec3),
390
+ invmass: wp.array(dtype=float),
391
+ spring_indices: wp.array(dtype=int),
392
+ spring_rest_lengths: wp.array(dtype=float),
393
+ spring_stiffness: wp.array(dtype=float),
394
+ spring_damping: wp.array(dtype=float),
395
+ dt: float,
396
+ lambdas: wp.array(dtype=float),
397
+ delta: wp.array(dtype=wp.vec3),
398
+ ):
399
+ tid = wp.tid()
400
+
401
+ i = spring_indices[tid * 2 + 0]
402
+ j = spring_indices[tid * 2 + 1]
403
+
404
+ ke = spring_stiffness[tid]
405
+ kd = spring_damping[tid]
406
+ rest = spring_rest_lengths[tid]
407
+
408
+ xi = x[i]
409
+ xj = x[j]
410
+
411
+ vi = v[i]
412
+ vj = v[j]
413
+
414
+ xij = xi - xj
415
+ vij = vi - vj
416
+
417
+ l = wp.length(xij)
418
+
419
+ if l == 0.0:
420
+ return
421
+
422
+ n = xij / l
423
+
424
+ c = l - rest
425
+ grad_c_xi = n
426
+ grad_c_xj = -1.0 * n
427
+
428
+ wi = invmass[i]
429
+ wj = invmass[j]
430
+
431
+ denom = wi + wj
432
+
433
+ # Note strict inequality for damping -- 0 damping is ok
434
+ if denom <= 0.0 or ke <= 0.0 or kd < 0.0:
435
+ return
436
+
437
+ alpha = 1.0 / (ke * dt * dt)
438
+ gamma = kd / (ke * dt)
439
+
440
+ grad_c_dot_v = dt * wp.dot(grad_c_xi, vij) # Note: dt because from the paper we want x_i - x^n, not v...
441
+ dlambda = -1.0 * (c + alpha * lambdas[tid] + gamma * grad_c_dot_v) / ((1.0 + gamma) * denom + alpha)
442
+
443
+ dxi = wi * dlambda * grad_c_xi
444
+ dxj = wj * dlambda * grad_c_xj
445
+
446
+ lambdas[tid] = lambdas[tid] + dlambda
447
+
448
+ wp.atomic_add(delta, i, dxi)
449
+ wp.atomic_add(delta, j, dxj)
450
+
451
+
452
+ @wp.kernel
453
+ def bending_constraint(
454
+ x: wp.array(dtype=wp.vec3),
455
+ v: wp.array(dtype=wp.vec3),
456
+ invmass: wp.array(dtype=float),
457
+ indices: wp.array2d(dtype=int),
458
+ rest: wp.array(dtype=float),
459
+ bending_properties: wp.array2d(dtype=float),
460
+ dt: float,
461
+ lambdas: wp.array(dtype=float),
462
+ delta: wp.array(dtype=wp.vec3),
463
+ ):
464
+ tid = wp.tid()
465
+ eps = 1.0e-6
466
+
467
+ ke = bending_properties[tid, 0]
468
+ kd = bending_properties[tid, 1]
469
+
470
+ i = indices[tid, 0]
471
+ j = indices[tid, 1]
472
+ k = indices[tid, 2]
473
+ l = indices[tid, 3]
474
+
475
+ if i == -1 or j == -1 or k == -1 or l == -1:
476
+ return
477
+
478
+ rest_angle = rest[tid]
479
+
480
+ x1 = x[i]
481
+ x2 = x[j]
482
+ x3 = x[k]
483
+ x4 = x[l]
484
+
485
+ v1 = v[i]
486
+ v2 = v[j]
487
+ v3 = v[k]
488
+ v4 = v[l]
489
+
490
+ w1 = invmass[i]
491
+ w2 = invmass[j]
492
+ w3 = invmass[k]
493
+ w4 = invmass[l]
494
+
495
+ n1 = wp.cross(x3 - x1, x4 - x1) # normal to face 1
496
+ n2 = wp.cross(x4 - x2, x3 - x2) # normal to face 2
497
+
498
+ n1_length = wp.length(n1)
499
+ n2_length = wp.length(n2)
500
+
501
+ if n1_length < eps or n2_length < eps:
502
+ return
503
+
504
+ n1 /= n1_length
505
+ n2 /= n2_length
506
+
507
+ cos_theta = wp.dot(n1, n2)
508
+
509
+ e = x4 - x3
510
+ e_hat = wp.normalize(e)
511
+ e_length = wp.length(e)
512
+
513
+ derivative_flip = wp.sign(wp.dot(wp.cross(n1, n2), e))
514
+ derivative_flip *= -1.0
515
+ angle = wp.acos(cos_theta)
516
+
517
+ grad_x1 = n1 * e_length * derivative_flip
518
+ grad_x2 = n2 * e_length * derivative_flip
519
+ grad_x3 = (n1 * wp.dot(x1 - x4, e_hat) + n2 * wp.dot(x2 - x4, e_hat)) * derivative_flip
520
+ grad_x4 = (n1 * wp.dot(x3 - x1, e_hat) + n2 * wp.dot(x3 - x2, e_hat)) * derivative_flip
521
+ c = angle - rest_angle
522
+ denominator = (
523
+ w1 * wp.length_sq(grad_x1)
524
+ + w2 * wp.length_sq(grad_x2)
525
+ + w3 * wp.length_sq(grad_x3)
526
+ + w4 * wp.length_sq(grad_x4)
527
+ )
528
+
529
+ # Note strict inequality for damping -- 0 damping is ok
530
+ if denominator <= 0.0 or ke <= 0.0 or kd < 0.0:
531
+ return
532
+
533
+ alpha = 1.0 / (ke * dt * dt)
534
+ gamma = kd / (ke * dt)
535
+
536
+ grad_dot_v = dt * (wp.dot(grad_x1, v1) + wp.dot(grad_x2, v2) + wp.dot(grad_x3, v3) + wp.dot(grad_x4, v4))
537
+
538
+ dlambda = -1.0 * (c + alpha * lambdas[tid] + gamma * grad_dot_v) / ((1.0 + gamma) * denominator + alpha)
539
+
540
+ delta0 = w1 * dlambda * grad_x1
541
+ delta1 = w2 * dlambda * grad_x2
542
+ delta2 = w3 * dlambda * grad_x3
543
+ delta3 = w4 * dlambda * grad_x4
544
+
545
+ lambdas[tid] = lambdas[tid] + dlambda
546
+
547
+ wp.atomic_add(delta, i, delta0)
548
+ wp.atomic_add(delta, j, delta1)
549
+ wp.atomic_add(delta, k, delta2)
550
+ wp.atomic_add(delta, l, delta3)
551
+
552
+
553
+ @wp.kernel
554
+ def solve_tetrahedra(
555
+ x: wp.array(dtype=wp.vec3),
556
+ v: wp.array(dtype=wp.vec3),
557
+ inv_mass: wp.array(dtype=float),
558
+ indices: wp.array(dtype=int, ndim=2),
559
+ rest_matrix: wp.array(dtype=wp.mat33),
560
+ activation: wp.array(dtype=float),
561
+ materials: wp.array(dtype=float, ndim=2),
562
+ dt: float,
563
+ relaxation: float,
564
+ delta: wp.array(dtype=wp.vec3),
565
+ ):
566
+ tid = wp.tid()
567
+
568
+ i = indices[tid, 0]
569
+ j = indices[tid, 1]
570
+ k = indices[tid, 2]
571
+ l = indices[tid, 3]
572
+
573
+ # act = activation[tid]
574
+
575
+ # k_mu = materials[tid, 0]
576
+ # k_lambda = materials[tid, 1]
577
+ # k_damp = materials[tid, 2]
578
+
579
+ x0 = x[i]
580
+ x1 = x[j]
581
+ x2 = x[k]
582
+ x3 = x[l]
583
+
584
+ # v0 = v[i]
585
+ # v1 = v[j]
586
+ # v2 = v[k]
587
+ # v3 = v[l]
588
+
589
+ w0 = inv_mass[i]
590
+ w1 = inv_mass[j]
591
+ w2 = inv_mass[k]
592
+ w3 = inv_mass[l]
593
+
594
+ x10 = x1 - x0
595
+ x20 = x2 - x0
596
+ x30 = x3 - x0
597
+
598
+ Ds = wp.matrix_from_cols(x10, x20, x30)
599
+ Dm = rest_matrix[tid]
600
+ inv_QT = wp.transpose(Dm)
601
+
602
+ inv_rest_volume = wp.determinant(Dm) * 6.0
603
+
604
+ # F = Xs*Xm^-1
605
+ F = Ds * Dm
606
+
607
+ f1 = wp.vec3(F[0, 0], F[1, 0], F[2, 0])
608
+ f2 = wp.vec3(F[0, 1], F[1, 1], F[2, 1])
609
+ f3 = wp.vec3(F[0, 2], F[1, 2], F[2, 2])
610
+
611
+ tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
612
+
613
+ C = float(0.0)
614
+ dC = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
615
+ compliance = float(0.0)
616
+
617
+ stretching_compliance = relaxation
618
+ volume_compliance = relaxation
619
+
620
+ num_terms = 2
621
+ for term in range(0, num_terms):
622
+ if term == 0:
623
+ # deviatoric, stable
624
+ C = tr - 3.0
625
+ dC = F * 2.0
626
+ compliance = stretching_compliance
627
+ elif term == 1:
628
+ # volume conservation
629
+ C = wp.determinant(F) - 1.0
630
+ dC = wp.matrix_from_cols(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))
631
+ compliance = volume_compliance
632
+
633
+ if C != 0.0:
634
+ dP = dC * inv_QT
635
+ grad1 = wp.vec3(dP[0][0], dP[1][0], dP[2][0])
636
+ grad2 = wp.vec3(dP[0][1], dP[1][1], dP[2][1])
637
+ grad3 = wp.vec3(dP[0][2], dP[1][2], dP[2][2])
638
+ grad0 = -grad1 - grad2 - grad3
639
+
640
+ w = (
641
+ wp.dot(grad0, grad0) * w0
642
+ + wp.dot(grad1, grad1) * w1
643
+ + wp.dot(grad2, grad2) * w2
644
+ + wp.dot(grad3, grad3) * w3
645
+ )
646
+
647
+ if w > 0.0:
648
+ alpha = compliance / dt / dt
649
+ if inv_rest_volume > 0.0:
650
+ alpha *= inv_rest_volume
651
+ dlambda = -C / (w + alpha)
652
+
653
+ wp.atomic_add(delta, i, w0 * dlambda * grad0)
654
+ wp.atomic_add(delta, j, w1 * dlambda * grad1)
655
+ wp.atomic_add(delta, k, w2 * dlambda * grad2)
656
+ wp.atomic_add(delta, l, w3 * dlambda * grad3)
657
+ # wp.atomic_add(particle.num_corr, id0, 1)
658
+ # wp.atomic_add(particle.num_corr, id1, 1)
659
+ # wp.atomic_add(particle.num_corr, id2, 1)
660
+ # wp.atomic_add(particle.num_corr, id3, 1)
661
+
662
+ # C_Spherical
663
+ # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
664
+ # r_s_inv = 1.0/r_s
665
+ # C = r_s - wp.sqrt(3.0)
666
+ # dCdx = F*wp.transpose(Dm)*r_s_inv
667
+ # alpha = 1.0
668
+
669
+ # C_D
670
+ # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
671
+ # C = r_s*r_s - 3.0
672
+ # dCdx = F*wp.transpose(Dm)*2.0
673
+ # alpha = 1.0
674
+
675
+ # grad1 = wp.vec3(dCdx[0, 0], dCdx[1, 0], dCdx[2, 0])
676
+ # grad2 = wp.vec3(dCdx[0, 1], dCdx[1, 1], dCdx[2, 1])
677
+ # grad3 = wp.vec3(dCdx[0, 2], dCdx[1, 2], dCdx[2, 2])
678
+ # grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0)
679
+
680
+ # denom = (
681
+ # wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
682
+ # )
683
+ # multiplier = C / (denom + 1.0 / (k_mu * dt * dt * rest_volume))
684
+
685
+ # delta0 = grad0 * multiplier
686
+ # delta1 = grad1 * multiplier
687
+ # delta2 = grad2 * multiplier
688
+ # delta3 = grad3 * multiplier
689
+
690
+ # # hydrostatic part
691
+ # J = wp.determinant(F)
692
+
693
+ # C_vol = J - alpha
694
+ # # dCdx = wp.matrix_from_cols(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))*wp.transpose(Dm)
695
+
696
+ # # grad1 = wp.vec3(dCdx[0,0], dCdx[1,0], dCdx[2,0])
697
+ # # grad2 = wp.vec3(dCdx[0,1], dCdx[1,1], dCdx[2,1])
698
+ # # grad3 = wp.vec3(dCdx[0,2], dCdx[1,2], dCdx[2,2])
699
+ # # grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0)
700
+
701
+ # s = inv_rest_volume / 6.0
702
+ # grad1 = wp.cross(x20, x30) * s
703
+ # grad2 = wp.cross(x30, x10) * s
704
+ # grad3 = wp.cross(x10, x20) * s
705
+ # grad0 = -(grad1 + grad2 + grad3)
706
+
707
+ # denom = (
708
+ # wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
709
+ # )
710
+ # multiplier = C_vol / (denom + 1.0 / (k_lambda * dt * dt * rest_volume))
711
+
712
+ # delta0 += grad0 * multiplier
713
+ # delta1 += grad1 * multiplier
714
+ # delta2 += grad2 * multiplier
715
+ # delta3 += grad3 * multiplier
716
+
717
+ # # # apply forces
718
+ # # wp.atomic_sub(delta, i, delta0 * w0 * relaxation)
719
+ # # wp.atomic_sub(delta, j, delta1 * w1 * relaxation)
720
+ # # wp.atomic_sub(delta, k, delta2 * w2 * relaxation)
721
+ # # wp.atomic_sub(delta, l, delta3 * w3 * relaxation)
722
+
723
+
724
+ @wp.kernel
725
+ def solve_tetrahedra2(
726
+ x: wp.array(dtype=wp.vec3),
727
+ v: wp.array(dtype=wp.vec3),
728
+ inv_mass: wp.array(dtype=float),
729
+ indices: wp.array(dtype=int, ndim=2),
730
+ pose: wp.array(dtype=wp.mat33),
731
+ activation: wp.array(dtype=float),
732
+ materials: wp.array(dtype=float, ndim=2),
733
+ dt: float,
734
+ relaxation: float,
735
+ delta: wp.array(dtype=wp.vec3),
736
+ ):
737
+ tid = wp.tid()
738
+
739
+ i = indices[tid, 0]
740
+ j = indices[tid, 1]
741
+ k = indices[tid, 2]
742
+ l = indices[tid, 3]
743
+
744
+ # act = activation[tid]
745
+
746
+ k_mu = materials[tid, 0]
747
+ k_lambda = materials[tid, 1]
748
+ # k_damp = materials[tid, 2]
749
+
750
+ x0 = x[i]
751
+ x1 = x[j]
752
+ x2 = x[k]
753
+ x3 = x[l]
754
+
755
+ # v0 = v[i]
756
+ # v1 = v[j]
757
+ # v2 = v[k]
758
+ # v3 = v[l]
759
+
760
+ w0 = inv_mass[i]
761
+ w1 = inv_mass[j]
762
+ w2 = inv_mass[k]
763
+ w3 = inv_mass[l]
764
+
765
+ x10 = x1 - x0
766
+ x20 = x2 - x0
767
+ x30 = x3 - x0
768
+
769
+ Ds = wp.matrix_from_cols(x10, x20, x30)
770
+ Dm = pose[tid]
771
+
772
+ inv_rest_volume = wp.determinant(Dm) * 6.0
773
+ rest_volume = 1.0 / inv_rest_volume
774
+
775
+ # F = Xs*Xm^-1
776
+ F = Ds * Dm
777
+
778
+ f1 = wp.vec3(F[0, 0], F[1, 0], F[2, 0])
779
+ f2 = wp.vec3(F[0, 1], F[1, 1], F[2, 1])
780
+ f3 = wp.vec3(F[0, 2], F[1, 2], F[2, 2])
781
+
782
+ # C_sqrt
783
+ # tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
784
+ # r_s = wp.sqrt(abs(tr - 3.0))
785
+ # C = r_s
786
+
787
+ # if (r_s == 0.0):
788
+ # return
789
+
790
+ # if (tr < 3.0):
791
+ # r_s = 0.0 - r_s
792
+
793
+ # dCdx = F*wp.transpose(Dm)*(1.0/r_s)
794
+ # alpha = 1.0 + k_mu / k_lambda
795
+
796
+ # C_Neo
797
+ r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
798
+ if r_s == 0.0:
799
+ return
800
+ # tr = wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3)
801
+ # if (tr < 3.0):
802
+ # r_s = -r_s
803
+ r_s_inv = 1.0 / r_s
804
+ C = r_s
805
+ dCdx = F * wp.transpose(Dm) * r_s_inv
806
+ alpha = 1.0 + k_mu / k_lambda
807
+
808
+ # C_Spherical
809
+ # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
810
+ # r_s_inv = 1.0/r_s
811
+ # C = r_s - wp.sqrt(3.0)
812
+ # dCdx = F*wp.transpose(Dm)*r_s_inv
813
+ # alpha = 1.0
814
+
815
+ # C_D
816
+ # r_s = wp.sqrt(wp.dot(f1, f1) + wp.dot(f2, f2) + wp.dot(f3, f3))
817
+ # C = r_s*r_s - 3.0
818
+ # dCdx = F*wp.transpose(Dm)*2.0
819
+ # alpha = 1.0
820
+
821
+ grad1 = wp.vec3(dCdx[0, 0], dCdx[1, 0], dCdx[2, 0])
822
+ grad2 = wp.vec3(dCdx[0, 1], dCdx[1, 1], dCdx[2, 1])
823
+ grad3 = wp.vec3(dCdx[0, 2], dCdx[1, 2], dCdx[2, 2])
824
+ grad0 = (grad1 + grad2 + grad3) * (0.0 - 1.0)
825
+
826
+ denom = (
827
+ wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
828
+ )
829
+ multiplier = C / (denom + 1.0 / (k_mu * dt * dt * rest_volume))
830
+
831
+ delta0 = grad0 * multiplier
832
+ delta1 = grad1 * multiplier
833
+ delta2 = grad2 * multiplier
834
+ delta3 = grad3 * multiplier
835
+
836
+ # hydrostatic part
837
+ J = wp.determinant(F)
838
+
839
+ C_vol = J - alpha
840
+ # dCdx = wp.matrix_from_cols(wp.cross(f2, f3), wp.cross(f3, f1), wp.cross(f1, f2))*wp.transpose(Dm)
841
+
842
+ # grad1 = wp.vec3(dCdx[0,0], dCdx[1,0], dCdx[2,0])
843
+ # grad2 = wp.vec3(dCdx[0,1], dCdx[1,1], dCdx[2,1])
844
+ # grad3 = wp.vec3(dCdx[0,2], dCdx[1,2], dCdx[2,2])
845
+ # grad0 = (grad1 + grad2 + grad3)*(0.0 - 1.0)
846
+
847
+ s = inv_rest_volume / 6.0
848
+ grad1 = wp.cross(x20, x30) * s
849
+ grad2 = wp.cross(x30, x10) * s
850
+ grad3 = wp.cross(x10, x20) * s
851
+ grad0 = -(grad1 + grad2 + grad3)
852
+
853
+ denom = (
854
+ wp.dot(grad0, grad0) * w0 + wp.dot(grad1, grad1) * w1 + wp.dot(grad2, grad2) * w2 + wp.dot(grad3, grad3) * w3
855
+ )
856
+ multiplier = C_vol / (denom + 1.0 / (k_lambda * dt * dt * rest_volume))
857
+
858
+ delta0 += grad0 * multiplier
859
+ delta1 += grad1 * multiplier
860
+ delta2 += grad2 * multiplier
861
+ delta3 += grad3 * multiplier
862
+
863
+ # apply forces
864
+ wp.atomic_sub(delta, i, delta0 * w0 * relaxation)
865
+ wp.atomic_sub(delta, j, delta1 * w1 * relaxation)
866
+ wp.atomic_sub(delta, k, delta2 * w2 * relaxation)
867
+ wp.atomic_sub(delta, l, delta3 * w3 * relaxation)
868
+
869
+
870
+ @wp.kernel
871
+ def apply_particle_deltas(
872
+ x_orig: wp.array(dtype=wp.vec3),
873
+ x_pred: wp.array(dtype=wp.vec3),
874
+ particle_flags: wp.array(dtype=wp.uint32),
875
+ delta: wp.array(dtype=wp.vec3),
876
+ dt: float,
877
+ v_max: float,
878
+ x_out: wp.array(dtype=wp.vec3),
879
+ v_out: wp.array(dtype=wp.vec3),
880
+ ):
881
+ tid = wp.tid()
882
+ if (particle_flags[tid] & PARTICLE_FLAG_ACTIVE) == 0:
883
+ return
884
+
885
+ x0 = x_orig[tid]
886
+ xp = x_pred[tid]
887
+
888
+ # constraint deltas
889
+ d = delta[tid]
890
+
891
+ x_new = xp + d
892
+ v_new = (x_new - x0) / dt
893
+
894
+ # enforce velocity limit to prevent instability
895
+ v_new_mag = wp.length(v_new)
896
+ if v_new_mag > v_max:
897
+ v_new *= v_max / v_new_mag
898
+
899
+ x_out[tid] = x_new
900
+ v_out[tid] = v_new
901
+
902
+
903
+ @wp.kernel
904
+ def apply_body_deltas(
905
+ q_in: wp.array(dtype=wp.transform),
906
+ qd_in: wp.array(dtype=wp.spatial_vector),
907
+ body_com: wp.array(dtype=wp.vec3),
908
+ body_I: wp.array(dtype=wp.mat33),
909
+ body_inv_m: wp.array(dtype=float),
910
+ body_inv_I: wp.array(dtype=wp.mat33),
911
+ deltas: wp.array(dtype=wp.spatial_vector),
912
+ constraint_inv_weights: wp.array(dtype=float),
913
+ dt: float,
914
+ # outputs
915
+ q_out: wp.array(dtype=wp.transform),
916
+ qd_out: wp.array(dtype=wp.spatial_vector),
917
+ ):
918
+ tid = wp.tid()
919
+ inv_m = body_inv_m[tid]
920
+ if inv_m == 0.0:
921
+ q_out[tid] = q_in[tid]
922
+ qd_out[tid] = qd_in[tid]
923
+ return
924
+ inv_I = body_inv_I[tid]
925
+
926
+ tf = q_in[tid]
927
+ delta = deltas[tid]
928
+
929
+ p0 = wp.transform_get_translation(tf)
930
+ q0 = wp.transform_get_rotation(tf)
931
+
932
+ weight = 1.0
933
+ if constraint_inv_weights:
934
+ inv_weight = constraint_inv_weights[tid]
935
+ if inv_weight > 0.0:
936
+ weight = 1.0 / inv_weight
937
+
938
+ dp = wp.spatial_bottom(delta) * (inv_m * weight)
939
+ dq = wp.spatial_top(delta) * weight
940
+ dq = wp.quat_rotate(q0, inv_I * wp.quat_rotate_inv(q0, dq))
941
+
942
+ # update orientation
943
+ q1 = q0 + 0.5 * wp.quat(dq * dt, 0.0) * q0
944
+ q1 = wp.normalize(q1)
945
+
946
+ # update position
947
+ com = body_com[tid]
948
+ x_com = p0 + wp.quat_rotate(q0, com)
949
+ p1 = x_com + dp * dt
950
+ p1 -= wp.quat_rotate(q1, com)
951
+
952
+ q_out[tid] = wp.transform(p1, q1)
953
+
954
+ v0 = wp.spatial_bottom(qd_in[tid])
955
+ w0 = wp.spatial_top(qd_in[tid])
956
+
957
+ # update linear and angular velocity
958
+ v1 = v0 + dp
959
+ # angular part (compute in body frame)
960
+ wb = wp.quat_rotate_inv(q0, w0 + dq)
961
+ tb = -wp.cross(wb, body_I[tid] * wb) # coriolis forces
962
+ w1 = wp.quat_rotate(q0, wb + inv_I * tb * dt)
963
+
964
+ # XXX this improves gradient stability
965
+ if wp.length(v1) < 1e-4:
966
+ v1 = wp.vec3(0.0)
967
+ if wp.length(w1) < 1e-4:
968
+ w1 = wp.vec3(0.0)
969
+
970
+ qd_out[tid] = wp.spatial_vector(w1, v1)
971
+
972
+
973
+ @wp.kernel
974
+ def apply_body_delta_velocities(
975
+ deltas: wp.array(dtype=wp.spatial_vector),
976
+ qd_out: wp.array(dtype=wp.spatial_vector),
977
+ ):
978
+ tid = wp.tid()
979
+ wp.atomic_add(qd_out, tid, deltas[tid])
980
+
981
+
982
+ @wp.kernel
983
+ def apply_joint_actions(
984
+ body_q: wp.array(dtype=wp.transform),
985
+ body_com: wp.array(dtype=wp.vec3),
986
+ joint_type: wp.array(dtype=int),
987
+ joint_parent: wp.array(dtype=int),
988
+ joint_child: wp.array(dtype=int),
989
+ joint_X_p: wp.array(dtype=wp.transform),
990
+ joint_X_c: wp.array(dtype=wp.transform),
991
+ joint_axis_start: wp.array(dtype=int),
992
+ joint_axis_dim: wp.array(dtype=int, ndim=2),
993
+ joint_axis: wp.array(dtype=wp.vec3),
994
+ joint_axis_mode: wp.array(dtype=int),
995
+ joint_act: wp.array(dtype=float),
996
+ body_f: wp.array(dtype=wp.spatial_vector),
997
+ ):
998
+ tid = wp.tid()
999
+ type = joint_type[tid]
1000
+ if type == wp.sim.JOINT_FIXED:
1001
+ return
1002
+ if type == wp.sim.JOINT_FREE:
1003
+ return
1004
+ if type == wp.sim.JOINT_DISTANCE:
1005
+ return
1006
+ if type == wp.sim.JOINT_BALL:
1007
+ return
1008
+
1009
+ # rigid body indices of the child and parent
1010
+ id_c = joint_child[tid]
1011
+ id_p = joint_parent[tid]
1012
+
1013
+ X_pj = joint_X_p[tid]
1014
+ # X_cj = joint_X_c[tid]
1015
+
1016
+ X_wp = X_pj
1017
+ pose_p = X_pj
1018
+ com_p = wp.vec3(0.0)
1019
+ # parent transform and moment arm
1020
+ if id_p >= 0:
1021
+ pose_p = body_q[id_p]
1022
+ X_wp = pose_p * X_wp
1023
+ com_p = body_com[id_p]
1024
+ r_p = wp.transform_get_translation(X_wp) - wp.transform_point(pose_p, com_p)
1025
+
1026
+ # child transform and moment arm
1027
+ pose_c = body_q[id_c]
1028
+ X_wc = pose_c
1029
+ com_c = body_com[id_c]
1030
+ r_c = wp.transform_get_translation(X_wc) - wp.transform_point(pose_c, com_c)
1031
+
1032
+ # # local joint rotations
1033
+ # q_p = wp.transform_get_rotation(X_wp)
1034
+ # q_c = wp.transform_get_rotation(X_wc)
1035
+
1036
+ # joint properties (for 1D joints)
1037
+ axis_start = joint_axis_start[tid]
1038
+ lin_axis_count = joint_axis_dim[tid, 0]
1039
+ ang_axis_count = joint_axis_dim[tid, 1]
1040
+
1041
+ # total force/torque on the parent
1042
+ t_total = wp.vec3()
1043
+ f_total = wp.vec3()
1044
+
1045
+ # handle angular constraints
1046
+ if type == wp.sim.JOINT_REVOLUTE:
1047
+ mode = joint_axis_mode[axis_start]
1048
+ if mode == wp.sim.JOINT_MODE_FORCE:
1049
+ axis = joint_axis[axis_start]
1050
+ act = joint_act[axis_start]
1051
+ a_p = wp.transform_vector(X_wp, axis)
1052
+ t_total += act * a_p
1053
+ elif type == wp.sim.JOINT_PRISMATIC:
1054
+ mode = joint_axis_mode[axis_start]
1055
+ if mode == wp.sim.JOINT_MODE_FORCE:
1056
+ axis = joint_axis[axis_start]
1057
+ act = joint_act[axis_start]
1058
+ a_p = wp.transform_vector(X_wp, axis)
1059
+ f_total += act * a_p
1060
+ elif type == wp.sim.JOINT_COMPOUND:
1061
+ # q_off = wp.transform_get_rotation(X_cj)
1062
+ # q_pc = wp.quat_inverse(q_off)*wp.quat_inverse(q_p)*q_c*q_off
1063
+ # # decompose to a compound rotation each axis
1064
+ # angles = quat_decompose(q_pc)
1065
+
1066
+ # # reconstruct rotation axes
1067
+ # axis_0 = wp.vec3(1.0, 0.0, 0.0)
1068
+ # q_0 = wp.quat_from_axis_angle(axis_0, angles[0])
1069
+
1070
+ # axis_1 = wp.quat_rotate(q_0, wp.vec3(0.0, 1.0, 0.0))
1071
+ # q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1072
+
1073
+ # axis_2 = wp.quat_rotate(q_1*q_0, wp.vec3(0.0, 0.0, 1.0))
1074
+
1075
+ # q_w = q_p*q_off
1076
+ # t_total += joint_act[qd_start+0] * wp.quat_rotate(q_w, axis_0)
1077
+ # t_total += joint_act[qd_start+1] * wp.quat_rotate(q_w, axis_1)
1078
+ # t_total += joint_act[qd_start+2] * wp.quat_rotate(q_w, axis_2)
1079
+
1080
+ if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1081
+ axis_0 = joint_axis[axis_start + 0]
1082
+ t_total += joint_act[axis_start + 0] * wp.transform_vector(X_wp, axis_0)
1083
+ if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1084
+ axis_1 = joint_axis[axis_start + 1]
1085
+ t_total += joint_act[axis_start + 1] * wp.transform_vector(X_wp, axis_1)
1086
+ if joint_axis_mode[axis_start + 2] == wp.sim.JOINT_MODE_FORCE:
1087
+ axis_2 = joint_axis[axis_start + 2]
1088
+ t_total += joint_act[axis_start + 2] * wp.transform_vector(X_wp, axis_2)
1089
+
1090
+ elif type == wp.sim.JOINT_UNIVERSAL:
1091
+ # q_off = wp.transform_get_rotation(X_cj)
1092
+ # q_pc = wp.quat_inverse(q_off)*wp.quat_inverse(q_p)*q_c*q_off
1093
+
1094
+ # # decompose to a compound rotation each axis
1095
+ # angles = quat_decompose(q_pc)
1096
+
1097
+ # # reconstruct rotation axes
1098
+ # axis_0 = wp.vec3(1.0, 0.0, 0.0)
1099
+ # q_0 = wp.quat_from_axis_angle(axis_0, angles[0])
1100
+
1101
+ # axis_1 = wp.quat_rotate(q_0, wp.vec3(0.0, 1.0, 0.0))
1102
+ # q_1 = wp.quat_from_axis_angle(axis_1, angles[1])
1103
+
1104
+ # axis_2 = wp.quat_rotate(q_1*q_0, wp.vec3(0.0, 0.0, 1.0))
1105
+
1106
+ # q_w = q_p*q_off
1107
+
1108
+ # free axes
1109
+ # t_total += joint_act[qd_start+0] * wp.quat_rotate(q_w, axis_0)
1110
+ # t_total += joint_act[qd_start+1] * wp.quat_rotate(q_w, axis_1)
1111
+
1112
+ if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1113
+ axis_0 = joint_axis[axis_start + 0]
1114
+ t_total += joint_act[axis_start + 0] * wp.transform_vector(X_wp, axis_0)
1115
+ if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1116
+ axis_1 = joint_axis[axis_start + 1]
1117
+ t_total += joint_act[axis_start + 1] * wp.transform_vector(X_wp, axis_1)
1118
+
1119
+ elif type == wp.sim.JOINT_D6:
1120
+ # unroll for loop to ensure joint actions remain differentiable
1121
+ # (since differentiating through a dynamic for loop that updates a local variable is not supported)
1122
+
1123
+ if lin_axis_count > 0:
1124
+ if joint_axis_mode[axis_start + 0] == wp.sim.JOINT_MODE_FORCE:
1125
+ axis = joint_axis[axis_start + 0]
1126
+ act = joint_act[axis_start + 0]
1127
+ a_p = wp.transform_vector(X_wp, axis)
1128
+ f_total += act * a_p
1129
+ if lin_axis_count > 1:
1130
+ if joint_axis_mode[axis_start + 1] == wp.sim.JOINT_MODE_FORCE:
1131
+ axis = joint_axis[axis_start + 1]
1132
+ act = joint_act[axis_start + 1]
1133
+ a_p = wp.transform_vector(X_wp, axis)
1134
+ f_total += act * a_p
1135
+ if lin_axis_count > 2:
1136
+ if joint_axis_mode[axis_start + 2] == wp.sim.JOINT_MODE_FORCE:
1137
+ axis = joint_axis[axis_start + 2]
1138
+ act = joint_act[axis_start + 2]
1139
+ a_p = wp.transform_vector(X_wp, axis)
1140
+ f_total += act * a_p
1141
+
1142
+ if ang_axis_count > 0:
1143
+ if joint_axis_mode[axis_start + lin_axis_count + 0] == wp.sim.JOINT_MODE_FORCE:
1144
+ axis = joint_axis[axis_start + lin_axis_count + 0]
1145
+ act = joint_act[axis_start + lin_axis_count + 0]
1146
+ a_p = wp.transform_vector(X_wp, axis)
1147
+ t_total += act * a_p
1148
+ if ang_axis_count > 1:
1149
+ if joint_axis_mode[axis_start + lin_axis_count + 1] == wp.sim.JOINT_MODE_FORCE:
1150
+ axis = joint_axis[axis_start + lin_axis_count + 1]
1151
+ act = joint_act[axis_start + lin_axis_count + 1]
1152
+ a_p = wp.transform_vector(X_wp, axis)
1153
+ t_total += act * a_p
1154
+ if ang_axis_count > 2:
1155
+ if joint_axis_mode[axis_start + lin_axis_count + 2] == wp.sim.JOINT_MODE_FORCE:
1156
+ axis = joint_axis[axis_start + lin_axis_count + 2]
1157
+ act = joint_act[axis_start + lin_axis_count + 2]
1158
+ a_p = wp.transform_vector(X_wp, axis)
1159
+ t_total += act * a_p
1160
+
1161
+ else:
1162
+ print("joint type not handled in apply_joint_actions")
1163
+
1164
+ # write forces
1165
+ if id_p >= 0:
1166
+ wp.atomic_sub(body_f, id_p, wp.spatial_vector(t_total + wp.cross(r_p, f_total), f_total))
1167
+ wp.atomic_add(body_f, id_c, wp.spatial_vector(t_total + wp.cross(r_c, f_total), f_total))
1168
+
1169
+
1170
+ @wp.func
1171
+ def update_joint_axis_mode(mode: wp.int32, axis: wp.vec3, input_axis_mode: wp.vec3i):
1172
+ # update the 3D axis mode flags given the axis vector and mode of this axis
1173
+ mode_x = wp.max(wp.int32(wp.nonzero(axis[0])) * mode, input_axis_mode[0])
1174
+ mode_y = wp.max(wp.int32(wp.nonzero(axis[1])) * mode, input_axis_mode[1])
1175
+ mode_z = wp.max(wp.int32(wp.nonzero(axis[2])) * mode, input_axis_mode[2])
1176
+ return wp.vec3i(mode_x, mode_y, mode_z)
1177
+
1178
+
1179
+ @wp.func
1180
+ def update_joint_axis_limits(axis: wp.vec3, limit_lower: float, limit_upper: float, input_limits: wp.spatial_vector):
1181
+ # update the 3D linear/angular limits (spatial_vector [lower, upper]) given the axis vector and limits
1182
+ lo_temp = axis * limit_lower
1183
+ up_temp = axis * limit_upper
1184
+ lo = vec_min(lo_temp, up_temp)
1185
+ up = vec_max(lo_temp, up_temp)
1186
+ input_lower = wp.spatial_top(input_limits)
1187
+ input_upper = wp.spatial_bottom(input_limits)
1188
+ lower = vec_min(input_lower, lo)
1189
+ upper = vec_max(input_upper, up)
1190
+ return wp.spatial_vector(lower, upper)
1191
+
1192
+
1193
+ @wp.func
1194
+ def update_joint_axis_target_ke_kd(
1195
+ axis: wp.vec3, target: float, target_ke: float, target_kd: float, input_target_ke_kd: wp.mat33
1196
+ ):
1197
+ # update the 3D linear/angular target, target_ke, and target_kd (mat33 [target, ke, kd]) given the axis vector and target, target_ke, target_kd
1198
+ axis_target = input_target_ke_kd[0]
1199
+ axis_ke = input_target_ke_kd[1]
1200
+ axis_kd = input_target_ke_kd[2]
1201
+ stiffness = axis * target_ke
1202
+ axis_target += stiffness * target # weighted target (to be normalized later by sum of target_ke)
1203
+ axis_ke += vec_abs(stiffness)
1204
+ axis_kd += vec_abs(axis * target_kd)
1205
+ return wp.mat33(
1206
+ axis_target[0],
1207
+ axis_target[1],
1208
+ axis_target[2],
1209
+ axis_ke[0],
1210
+ axis_ke[1],
1211
+ axis_ke[2],
1212
+ axis_kd[0],
1213
+ axis_kd[1],
1214
+ axis_kd[2],
1215
+ )
1216
+
1217
+
1218
+ @wp.func
1219
+ def compute_linear_correction_3d(
1220
+ dx: wp.vec3,
1221
+ r1: wp.vec3,
1222
+ r2: wp.vec3,
1223
+ tf1: wp.transform,
1224
+ tf2: wp.transform,
1225
+ m_inv1: float,
1226
+ m_inv2: float,
1227
+ I_inv1: wp.mat33,
1228
+ I_inv2: wp.mat33,
1229
+ lambda_in: float,
1230
+ compliance: float,
1231
+ damping: float,
1232
+ dt: float,
1233
+ ) -> float:
1234
+ c = wp.length(dx)
1235
+ if c == 0.0:
1236
+ # print("c == 0.0 in positional correction")
1237
+ return 0.0
1238
+
1239
+ n = wp.normalize(dx)
1240
+
1241
+ q1 = wp.transform_get_rotation(tf1)
1242
+ q2 = wp.transform_get_rotation(tf2)
1243
+
1244
+ # Eq. 2-3 (make sure to project into the frame of the body)
1245
+ r1xn = wp.quat_rotate_inv(q1, wp.cross(r1, n))
1246
+ r2xn = wp.quat_rotate_inv(q2, wp.cross(r2, n))
1247
+
1248
+ w1 = m_inv1 + wp.dot(r1xn, I_inv1 * r1xn)
1249
+ w2 = m_inv2 + wp.dot(r2xn, I_inv2 * r2xn)
1250
+ w = w1 + w2
1251
+ if w == 0.0:
1252
+ return 0.0
1253
+ alpha = compliance
1254
+ gamma = compliance * damping
1255
+
1256
+ # Eq. 4-5
1257
+ d_lambda = -c - alpha * lambda_in
1258
+ # TODO consider damping for velocity correction?
1259
+ # delta_lambda = -(err + alpha * lambda_in + gamma * derr)
1260
+ if w + alpha > 0.0:
1261
+ d_lambda /= w * (dt + gamma) + alpha / dt
1262
+
1263
+ return d_lambda
1264
+
1265
+
1266
+ @wp.func
1267
+ def compute_angular_correction_3d(
1268
+ corr: wp.vec3,
1269
+ q1: wp.quat,
1270
+ q2: wp.quat,
1271
+ m_inv1: float,
1272
+ m_inv2: float,
1273
+ I_inv1: wp.mat33,
1274
+ I_inv2: wp.mat33,
1275
+ alpha_tilde: float,
1276
+ # lambda_prev: float,
1277
+ relaxation: float,
1278
+ dt: float,
1279
+ ):
1280
+ # compute and apply the correction impulse for an angular constraint
1281
+ theta = wp.length(corr)
1282
+ if theta == 0.0:
1283
+ return 0.0
1284
+
1285
+ n = wp.normalize(corr)
1286
+
1287
+ # project variables to body rest frame as they are in local matrix
1288
+ n1 = wp.quat_rotate_inv(q1, n)
1289
+ n2 = wp.quat_rotate_inv(q2, n)
1290
+
1291
+ # Eq. 11-12
1292
+ w1 = wp.dot(n1, I_inv1 * n1)
1293
+ w2 = wp.dot(n2, I_inv2 * n2)
1294
+ w = w1 + w2
1295
+ if w == 0.0:
1296
+ return 0.0
1297
+
1298
+ # Eq. 13-14
1299
+ lambda_prev = 0.0
1300
+ d_lambda = (-theta - alpha_tilde * lambda_prev) / (w * dt + alpha_tilde / dt)
1301
+ # TODO consider lambda_prev?
1302
+ # p = d_lambda * n * relaxation
1303
+
1304
+ # Eq. 15-16
1305
+ return d_lambda
1306
+
1307
+
1308
+ @wp.kernel
1309
+ def solve_simple_body_joints(
1310
+ body_q: wp.array(dtype=wp.transform),
1311
+ body_qd: wp.array(dtype=wp.spatial_vector),
1312
+ body_com: wp.array(dtype=wp.vec3),
1313
+ body_inv_m: wp.array(dtype=float),
1314
+ body_inv_I: wp.array(dtype=wp.mat33),
1315
+ joint_type: wp.array(dtype=int),
1316
+ joint_enabled: wp.array(dtype=int),
1317
+ joint_parent: wp.array(dtype=int),
1318
+ joint_child: wp.array(dtype=int),
1319
+ joint_X_p: wp.array(dtype=wp.transform),
1320
+ joint_X_c: wp.array(dtype=wp.transform),
1321
+ joint_limit_lower: wp.array(dtype=float),
1322
+ joint_limit_upper: wp.array(dtype=float),
1323
+ joint_axis_start: wp.array(dtype=int),
1324
+ joint_axis_dim: wp.array(dtype=int, ndim=2),
1325
+ joint_axis_mode: wp.array(dtype=int),
1326
+ joint_axis: wp.array(dtype=wp.vec3),
1327
+ joint_target: wp.array(dtype=float),
1328
+ joint_target_ke: wp.array(dtype=float),
1329
+ joint_target_kd: wp.array(dtype=float),
1330
+ joint_linear_compliance: wp.array(dtype=float),
1331
+ joint_angular_compliance: wp.array(dtype=float),
1332
+ angular_relaxation: float,
1333
+ linear_relaxation: float,
1334
+ dt: float,
1335
+ deltas: wp.array(dtype=wp.spatial_vector),
1336
+ ):
1337
+ tid = wp.tid()
1338
+ type = joint_type[tid]
1339
+
1340
+ if joint_enabled[tid] == 0:
1341
+ return
1342
+ if type == wp.sim.JOINT_FREE:
1343
+ return
1344
+ if type == wp.sim.JOINT_COMPOUND:
1345
+ return
1346
+ if type == wp.sim.JOINT_UNIVERSAL:
1347
+ return
1348
+ if type == wp.sim.JOINT_DISTANCE:
1349
+ return
1350
+ if type == wp.sim.JOINT_D6:
1351
+ return
1352
+
1353
+ # rigid body indices of the child and parent
1354
+ id_c = joint_child[tid]
1355
+ id_p = joint_parent[tid]
1356
+
1357
+ X_pj = joint_X_p[tid]
1358
+ X_cj = joint_X_c[tid]
1359
+
1360
+ X_wp = X_pj
1361
+ m_inv_p = 0.0
1362
+ I_inv_p = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1363
+ pose_p = X_pj
1364
+ com_p = wp.vec3(0.0)
1365
+ # parent transform and moment arm
1366
+ if id_p >= 0:
1367
+ pose_p = body_q[id_p]
1368
+ X_wp = pose_p * X_wp
1369
+ com_p = body_com[id_p]
1370
+ m_inv_p = body_inv_m[id_p]
1371
+ I_inv_p = body_inv_I[id_p]
1372
+ r_p = wp.transform_get_translation(X_wp) - wp.transform_point(pose_p, com_p)
1373
+
1374
+ # child transform and moment arm
1375
+ pose_c = body_q[id_c]
1376
+ X_wc = pose_c * X_cj
1377
+ com_c = body_com[id_c]
1378
+ m_inv_c = body_inv_m[id_c]
1379
+ I_inv_c = body_inv_I[id_c]
1380
+ r_c = wp.transform_get_translation(X_wc) - wp.transform_point(pose_c, com_c)
1381
+
1382
+ if m_inv_p == 0.0 and m_inv_c == 0.0:
1383
+ # connection between two immovable bodies
1384
+ return
1385
+
1386
+ # accumulate constraint deltas
1387
+ lin_delta_p = wp.vec3(0.0)
1388
+ ang_delta_p = wp.vec3(0.0)
1389
+ lin_delta_c = wp.vec3(0.0)
1390
+ ang_delta_c = wp.vec3(0.0)
1391
+
1392
+ # rel_pose = wp.transform_inverse(X_wp) * X_wc
1393
+ # rel_p = wp.transform_get_translation(rel_pose)
1394
+
1395
+ # joint connection points
1396
+ # x_p = wp.transform_get_translation(X_wp)
1397
+ x_c = wp.transform_get_translation(X_wc)
1398
+
1399
+ # linear_compliance = joint_linear_compliance[tid]
1400
+ angular_compliance = joint_angular_compliance[tid]
1401
+ damping = 0.0
1402
+
1403
+ axis_start = joint_axis_start[tid]
1404
+ # mode = joint_axis_mode[axis_start]
1405
+
1406
+ # local joint rotations
1407
+ q_p = wp.transform_get_rotation(X_wp)
1408
+ q_c = wp.transform_get_rotation(X_wc)
1409
+ inertial_q_p = wp.transform_get_rotation(pose_p)
1410
+ inertial_q_c = wp.transform_get_rotation(pose_c)
1411
+
1412
+ # joint properties (for 1D joints)
1413
+ axis = joint_axis[axis_start]
1414
+
1415
+ if type == wp.sim.JOINT_FIXED:
1416
+ limit_lower = 0.0
1417
+ limit_upper = 0.0
1418
+ else:
1419
+ limit_lower = joint_limit_lower[axis_start]
1420
+ limit_upper = joint_limit_upper[axis_start]
1421
+
1422
+ # linear_alpha_tilde = linear_compliance / dt / dt
1423
+ angular_alpha_tilde = angular_compliance / dt / dt
1424
+
1425
+ # prevent division by zero
1426
+ # linear_alpha_tilde = wp.max(linear_alpha_tilde, 1e-6)
1427
+ # angular_alpha_tilde = wp.max(angular_alpha_tilde, 1e-6)
1428
+
1429
+ # accumulate constraint deltas
1430
+ lin_delta_p = wp.vec3(0.0)
1431
+ ang_delta_p = wp.vec3(0.0)
1432
+ lin_delta_c = wp.vec3(0.0)
1433
+ ang_delta_c = wp.vec3(0.0)
1434
+
1435
+ # handle angular constraints
1436
+ if type == wp.sim.JOINT_REVOLUTE:
1437
+ # align joint axes
1438
+ a_p = wp.quat_rotate(q_p, axis)
1439
+ a_c = wp.quat_rotate(q_c, axis)
1440
+ # Eq. 20
1441
+ corr = wp.cross(a_p, a_c)
1442
+ ncorr = wp.normalize(corr)
1443
+
1444
+ angular_relaxation = 0.2
1445
+ # angular_correction(
1446
+ # corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1447
+ # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1448
+ lambda_n = compute_angular_correction_3d(
1449
+ corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, angular_alpha_tilde, damping, dt
1450
+ )
1451
+ lambda_n *= angular_relaxation
1452
+ ang_delta_p -= lambda_n * ncorr
1453
+ ang_delta_c += lambda_n * ncorr
1454
+
1455
+ # limit joint angles (Alg. 3)
1456
+ pi = 3.14159265359
1457
+ two_pi = 2.0 * pi
1458
+ if limit_lower > -two_pi or limit_upper < two_pi:
1459
+ # find a perpendicular vector to joint axis
1460
+ a = axis
1461
+ # https://math.stackexchange.com/a/3582461
1462
+ g = wp.sign(a[2])
1463
+ h = a[2] + g
1464
+ b = wp.vec3(g - a[0] * a[0] / h, -a[0] * a[1] / h, -a[0])
1465
+ c = wp.normalize(wp.cross(a, b))
1466
+ # b = c # TODO verify
1467
+
1468
+ # joint axis
1469
+ n = wp.quat_rotate(q_p, a)
1470
+ # the axes n1 and n2 are aligned with the two bodies
1471
+ n1 = wp.quat_rotate(q_p, b)
1472
+ n2 = wp.quat_rotate(q_c, b)
1473
+
1474
+ phi = wp.asin(wp.dot(wp.cross(n1, n2), n))
1475
+ # print("phi")
1476
+ # print(phi)
1477
+ if wp.dot(n1, n2) < 0.0:
1478
+ phi = pi - phi
1479
+ if phi > pi:
1480
+ phi -= two_pi
1481
+ if phi < -pi:
1482
+ phi += two_pi
1483
+ if phi < limit_lower or phi > limit_upper:
1484
+ phi = wp.clamp(phi, limit_lower, limit_upper)
1485
+ # print("clamped phi")
1486
+ # print(phi)
1487
+ # rot = wp.quat(phi, n[0], n[1], n[2])
1488
+ # rot = wp.quat(n, phi)
1489
+ rot = wp.quat_from_axis_angle(n, phi)
1490
+ n1 = wp.quat_rotate(rot, n1)
1491
+ corr = wp.cross(n1, n2)
1492
+ # print("corr")
1493
+ # print(corr)
1494
+ # TODO expose
1495
+ # angular_alpha_tilde = 0.0001 / dt / dt
1496
+ # angular_relaxation = 0.5
1497
+ # TODO fix this constraint
1498
+ # angular_correction(
1499
+ # corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1500
+ # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1501
+ lambda_n = compute_angular_correction_3d(
1502
+ corr,
1503
+ inertial_q_p,
1504
+ inertial_q_c,
1505
+ m_inv_p,
1506
+ m_inv_c,
1507
+ I_inv_p,
1508
+ I_inv_c,
1509
+ angular_alpha_tilde,
1510
+ damping,
1511
+ dt,
1512
+ )
1513
+ lambda_n *= angular_relaxation
1514
+ ncorr = wp.normalize(corr)
1515
+ ang_delta_p -= lambda_n * ncorr
1516
+ ang_delta_c += lambda_n * ncorr
1517
+
1518
+ # handle joint targets
1519
+ target_ke = joint_target_ke[axis_start]
1520
+ # target_kd = joint_target_kd[axis_start]
1521
+ target = joint_target[axis_start]
1522
+ if target_ke > 0.0:
1523
+ # find a perpendicular vector to joint axis
1524
+ a = axis
1525
+ # https://math.stackexchange.com/a/3582461
1526
+ g = wp.sign(a[2])
1527
+ h = a[2] + g
1528
+ b = wp.vec3(g - a[0] * a[0] / h, -a[0] * a[1] / h, -a[0])
1529
+ c = wp.normalize(wp.cross(a, b))
1530
+ b = c
1531
+
1532
+ q = wp.quat_from_axis_angle(a_p, target)
1533
+ b_target = wp.quat_rotate(q, wp.quat_rotate(q_p, b))
1534
+ b2 = wp.quat_rotate(q_c, b)
1535
+ # Eq. 21
1536
+ d_target = wp.cross(b_target, b2)
1537
+
1538
+ target_compliance = 1.0 / target_ke # / dt / dt
1539
+ # angular_correction(
1540
+ # d_target, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1541
+ # target_compliance, angular_relaxation, deltas, id_p, id_c)
1542
+ lambda_n = compute_angular_correction_3d(
1543
+ d_target, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, target_compliance, damping, dt
1544
+ )
1545
+ lambda_n *= angular_relaxation
1546
+ ncorr = wp.normalize(d_target)
1547
+ # TODO fix
1548
+ ang_delta_p -= lambda_n * ncorr
1549
+ ang_delta_c += lambda_n * ncorr
1550
+
1551
+ if (type == wp.sim.JOINT_FIXED) or (type == wp.sim.JOINT_PRISMATIC):
1552
+ # align the mutual orientations of the two bodies
1553
+ # Eq. 18-19
1554
+ q = q_p * wp.quat_inverse(q_c)
1555
+ corr = -2.0 * wp.vec3(q[0], q[1], q[2])
1556
+ # angular_correction(
1557
+ # -corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c,
1558
+ # angular_alpha_tilde, angular_relaxation, deltas, id_p, id_c)
1559
+ lambda_n = compute_angular_correction_3d(
1560
+ corr, inertial_q_p, inertial_q_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, angular_alpha_tilde, damping, dt
1561
+ )
1562
+ lambda_n *= angular_relaxation
1563
+ ncorr = wp.normalize(corr)
1564
+ ang_delta_p -= lambda_n * ncorr
1565
+ ang_delta_c += lambda_n * ncorr
1566
+
1567
+ # handle positional constraints
1568
+
1569
+ # joint connection points
1570
+ x_p = wp.transform_get_translation(X_wp)
1571
+ x_c = wp.transform_get_translation(X_wc)
1572
+
1573
+ # compute error between the joint attachment points on both bodies
1574
+ # delta x is the difference of point r_2 minus point r_1 (Fig. 3)
1575
+ dx = x_c - x_p
1576
+
1577
+ # rotate the error vector into the joint frame
1578
+ q_dx = q_p
1579
+ # q_dx = q_c
1580
+ # q_dx = wp.transform_get_rotation(pose_p)
1581
+ dx = wp.quat_rotate_inv(q_dx, dx)
1582
+
1583
+ lower_pos_limits = wp.vec3(0.0)
1584
+ upper_pos_limits = wp.vec3(0.0)
1585
+ if type == wp.sim.JOINT_PRISMATIC:
1586
+ lower_pos_limits = axis * limit_lower
1587
+ upper_pos_limits = axis * limit_upper
1588
+
1589
+ # compute linear constraint violations
1590
+ corr = wp.vec3(0.0)
1591
+ zero = wp.vec3(0.0)
1592
+ corr -= vec_leaky_min(zero, upper_pos_limits - dx)
1593
+ corr -= vec_leaky_max(zero, lower_pos_limits - dx)
1594
+
1595
+ # if (type == wp.sim.JOINT_PRISMATIC):
1596
+ # if mode == JOINT_MODE_TARGET_POSITION:
1597
+ # target = wp.clamp(target, limit_lower, limit_upper)
1598
+ # if target_ke > 0.0:
1599
+ # err = dx - target * axis
1600
+ # compliance = 1.0 / target_ke
1601
+ # damping = axis_damping[dim]
1602
+ # elif mode == JOINT_MODE_TARGET_VELOCITY:
1603
+ # if target_ke > 0.0:
1604
+ # err = (derr - target) * dt
1605
+ # compliance = 1.0 / target_ke
1606
+ # damping = axis_damping[dim]
1607
+
1608
+ # rotate correction vector into world frame
1609
+ corr = wp.quat_rotate(q_dx, corr)
1610
+
1611
+ lambda_in = 0.0
1612
+ linear_alpha = joint_linear_compliance[tid]
1613
+ lambda_n = compute_linear_correction_3d(
1614
+ corr, r_p, r_c, pose_p, pose_c, m_inv_p, m_inv_c, I_inv_p, I_inv_c, lambda_in, linear_alpha, damping, dt
1615
+ )
1616
+ lambda_n *= linear_relaxation
1617
+ n = wp.normalize(corr)
1618
+
1619
+ lin_delta_p -= n * lambda_n
1620
+ lin_delta_c += n * lambda_n
1621
+ ang_delta_p -= wp.cross(r_p, n) * lambda_n
1622
+ ang_delta_c += wp.cross(r_c, n) * lambda_n
1623
+
1624
+ if id_p >= 0:
1625
+ wp.atomic_add(deltas, id_p, wp.spatial_vector(ang_delta_p, lin_delta_p))
1626
+ if id_c >= 0:
1627
+ wp.atomic_add(deltas, id_c, wp.spatial_vector(ang_delta_c, lin_delta_c))
1628
+
1629
+
1630
+ @wp.kernel
1631
+ def solve_body_joints(
1632
+ body_q: wp.array(dtype=wp.transform),
1633
+ body_qd: wp.array(dtype=wp.spatial_vector),
1634
+ body_com: wp.array(dtype=wp.vec3),
1635
+ body_inv_m: wp.array(dtype=float),
1636
+ body_inv_I: wp.array(dtype=wp.mat33),
1637
+ joint_type: wp.array(dtype=int),
1638
+ joint_enabled: wp.array(dtype=int),
1639
+ joint_parent: wp.array(dtype=int),
1640
+ joint_child: wp.array(dtype=int),
1641
+ joint_X_p: wp.array(dtype=wp.transform),
1642
+ joint_X_c: wp.array(dtype=wp.transform),
1643
+ joint_limit_lower: wp.array(dtype=float),
1644
+ joint_limit_upper: wp.array(dtype=float),
1645
+ joint_axis_start: wp.array(dtype=int),
1646
+ joint_axis_dim: wp.array(dtype=int, ndim=2),
1647
+ joint_axis_mode: wp.array(dtype=int),
1648
+ joint_axis: wp.array(dtype=wp.vec3),
1649
+ joint_act: wp.array(dtype=float),
1650
+ joint_target_ke: wp.array(dtype=float),
1651
+ joint_target_kd: wp.array(dtype=float),
1652
+ joint_linear_compliance: wp.array(dtype=float),
1653
+ joint_angular_compliance: wp.array(dtype=float),
1654
+ angular_relaxation: float,
1655
+ linear_relaxation: float,
1656
+ dt: float,
1657
+ deltas: wp.array(dtype=wp.spatial_vector),
1658
+ ):
1659
+ tid = wp.tid()
1660
+ type = joint_type[tid]
1661
+
1662
+ if joint_enabled[tid] == 0:
1663
+ return
1664
+ if type == wp.sim.JOINT_FREE:
1665
+ return
1666
+ # if type == wp.sim.JOINT_FIXED:
1667
+ # return
1668
+ # if type == wp.sim.JOINT_REVOLUTE:
1669
+ # return
1670
+ # if type == wp.sim.JOINT_PRISMATIC:
1671
+ # return
1672
+ # if type == wp.sim.JOINT_BALL:
1673
+ # return
1674
+
1675
+ # rigid body indices of the child and parent
1676
+ id_c = joint_child[tid]
1677
+ id_p = joint_parent[tid]
1678
+
1679
+ X_pj = joint_X_p[tid]
1680
+ X_cj = joint_X_c[tid]
1681
+
1682
+ X_wp = X_pj
1683
+ m_inv_p = 0.0
1684
+ I_inv_p = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1685
+ pose_p = X_pj
1686
+ com_p = wp.vec3(0.0)
1687
+ vel_p = wp.vec3(0.0)
1688
+ omega_p = wp.vec3(0.0)
1689
+ # parent transform and moment arm
1690
+ if id_p >= 0:
1691
+ pose_p = body_q[id_p]
1692
+ X_wp = pose_p * X_wp
1693
+ com_p = body_com[id_p]
1694
+ m_inv_p = body_inv_m[id_p]
1695
+ I_inv_p = body_inv_I[id_p]
1696
+ vel_p = wp.spatial_bottom(body_qd[id_p])
1697
+ omega_p = wp.spatial_top(body_qd[id_p])
1698
+
1699
+ # child transform and moment arm
1700
+ pose_c = body_q[id_c]
1701
+ X_wc = pose_c * X_cj
1702
+ com_c = body_com[id_c]
1703
+ m_inv_c = body_inv_m[id_c]
1704
+ I_inv_c = body_inv_I[id_c]
1705
+ vel_c = wp.spatial_bottom(body_qd[id_c])
1706
+ omega_c = wp.spatial_top(body_qd[id_c])
1707
+
1708
+ if m_inv_p == 0.0 and m_inv_c == 0.0:
1709
+ # connection between two immovable bodies
1710
+ return
1711
+
1712
+ # accumulate constraint deltas
1713
+ lin_delta_p = wp.vec3(0.0)
1714
+ ang_delta_p = wp.vec3(0.0)
1715
+ lin_delta_c = wp.vec3(0.0)
1716
+ ang_delta_c = wp.vec3(0.0)
1717
+
1718
+ rel_pose = wp.transform_inverse(X_wp) * X_wc
1719
+ rel_p = wp.transform_get_translation(rel_pose)
1720
+
1721
+ # joint connection points
1722
+ # x_p = wp.transform_get_translation(X_wp)
1723
+ x_c = wp.transform_get_translation(X_wc)
1724
+
1725
+ linear_compliance = joint_linear_compliance[tid]
1726
+ angular_compliance = joint_angular_compliance[tid]
1727
+
1728
+ axis_start = joint_axis_start[tid]
1729
+ lin_axis_count = joint_axis_dim[tid, 0]
1730
+ ang_axis_count = joint_axis_dim[tid, 1]
1731
+
1732
+ world_com_p = wp.transform_point(pose_p, com_p)
1733
+ world_com_c = wp.transform_point(pose_c, com_c)
1734
+
1735
+ # handle positional constraints
1736
+ if type == wp.sim.JOINT_DISTANCE:
1737
+ r_p = wp.transform_get_translation(X_wp) - world_com_p
1738
+ r_c = wp.transform_get_translation(X_wc) - world_com_c
1739
+ lower = joint_limit_lower[axis_start]
1740
+ upper = joint_limit_upper[axis_start]
1741
+ if lower < 0.0 and upper < 0.0:
1742
+ # no limits
1743
+ return
1744
+ d = wp.length(rel_p)
1745
+ err = 0.0
1746
+ if lower >= 0.0 and d < lower:
1747
+ err = d - lower
1748
+ # use a more descriptive direction vector for the constraint
1749
+ # in case the joint parent and child anchors are very close
1750
+ rel_p = err * wp.normalize(world_com_c - world_com_p)
1751
+ elif upper >= 0.0 and d > upper:
1752
+ err = d - upper
1753
+
1754
+ if wp.abs(err) > 1e-9:
1755
+ # compute gradients
1756
+ linear_c = rel_p
1757
+ linear_p = -linear_c
1758
+ r_c = x_c - world_com_c
1759
+ angular_p = -wp.cross(r_p, linear_c)
1760
+ angular_c = wp.cross(r_c, linear_c)
1761
+ # constraint time derivative
1762
+ derr = (
1763
+ wp.dot(linear_p, vel_p)
1764
+ + wp.dot(linear_c, vel_c)
1765
+ + wp.dot(angular_p, omega_p)
1766
+ + wp.dot(angular_c, omega_c)
1767
+ )
1768
+ lambda_in = 0.0
1769
+ compliance = linear_compliance
1770
+ ke = joint_target_ke[axis_start]
1771
+ if ke > 0.0:
1772
+ compliance = 1.0 / ke
1773
+ damping = joint_target_kd[axis_start]
1774
+ d_lambda = compute_positional_correction(
1775
+ err,
1776
+ derr,
1777
+ pose_p,
1778
+ pose_c,
1779
+ m_inv_p,
1780
+ m_inv_c,
1781
+ I_inv_p,
1782
+ I_inv_c,
1783
+ linear_p,
1784
+ linear_c,
1785
+ angular_p,
1786
+ angular_c,
1787
+ lambda_in,
1788
+ compliance,
1789
+ damping,
1790
+ dt,
1791
+ )
1792
+
1793
+ lin_delta_p += linear_p * (d_lambda * linear_relaxation)
1794
+ ang_delta_p += angular_p * (d_lambda * angular_relaxation)
1795
+ lin_delta_c += linear_c * (d_lambda * linear_relaxation)
1796
+ ang_delta_c += angular_c * (d_lambda * angular_relaxation)
1797
+
1798
+ else:
1799
+ # compute joint target, stiffness, damping
1800
+ ke_sum = float(0.0)
1801
+ axis_limits = wp.spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
1802
+ axis_mode = wp.vec3i(0, 0, 0)
1803
+ axis_target_ke_kd = wp.mat33(0.0)
1804
+ # avoid a for loop here since local variables would need to be modified which is not yet differentiable
1805
+ if lin_axis_count > 0:
1806
+ axis = joint_axis[axis_start]
1807
+ lo_temp = axis * joint_limit_lower[axis_start]
1808
+ up_temp = axis * joint_limit_upper[axis_start]
1809
+ axis_limits = wp.spatial_vector(vec_min(lo_temp, up_temp), vec_max(lo_temp, up_temp))
1810
+ mode = joint_axis_mode[axis_start]
1811
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1812
+ ke = joint_target_ke[axis_start]
1813
+ kd = joint_target_kd[axis_start]
1814
+ target = joint_act[axis_start]
1815
+ axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1816
+ axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1817
+ ke_sum += ke
1818
+ if lin_axis_count > 1:
1819
+ axis_idx = axis_start + 1
1820
+ axis = joint_axis[axis_idx]
1821
+ lower = joint_limit_lower[axis_idx]
1822
+ upper = joint_limit_upper[axis_idx]
1823
+ axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1824
+ mode = joint_axis_mode[axis_idx]
1825
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1826
+ ke = joint_target_ke[axis_idx]
1827
+ kd = joint_target_kd[axis_idx]
1828
+ target = joint_act[axis_idx]
1829
+ axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1830
+ axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1831
+ ke_sum += ke
1832
+ if lin_axis_count > 2:
1833
+ axis_idx = axis_start + 2
1834
+ axis = joint_axis[axis_idx]
1835
+ lower = joint_limit_lower[axis_idx]
1836
+ upper = joint_limit_upper[axis_idx]
1837
+ axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
1838
+ mode = joint_axis_mode[axis_idx]
1839
+ if mode != JOINT_MODE_FORCE: # position or velocity target
1840
+ ke = joint_target_ke[axis_idx]
1841
+ kd = joint_target_kd[axis_idx]
1842
+ target = joint_act[axis_idx]
1843
+ axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
1844
+ axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
1845
+ ke_sum += ke
1846
+
1847
+ axis_target = axis_target_ke_kd[0]
1848
+ axis_stiffness = axis_target_ke_kd[1]
1849
+ axis_damping = axis_target_ke_kd[2]
1850
+ if ke_sum > 0.0:
1851
+ axis_target /= ke_sum
1852
+ axis_limits_lower = wp.spatial_top(axis_limits)
1853
+ axis_limits_upper = wp.spatial_bottom(axis_limits)
1854
+
1855
+ frame_p = wp.quat_to_matrix(wp.transform_get_rotation(X_wp))
1856
+ # note that x_c appearing in both is correct
1857
+ r_p = x_c - world_com_p
1858
+ r_c = x_c - wp.transform_point(pose_c, com_c)
1859
+
1860
+ # for loop will be unrolled, so we can modify local variables
1861
+ for dim in range(3):
1862
+ e = rel_p[dim]
1863
+ mode = axis_mode[dim]
1864
+
1865
+ # compute gradients
1866
+ linear_c = wp.vec3(frame_p[0, dim], frame_p[1, dim], frame_p[2, dim])
1867
+ linear_p = -linear_c
1868
+ angular_p = -wp.cross(r_p, linear_c)
1869
+ angular_c = wp.cross(r_c, linear_c)
1870
+ # constraint time derivative
1871
+ derr = (
1872
+ wp.dot(linear_p, vel_p)
1873
+ + wp.dot(linear_c, vel_c)
1874
+ + wp.dot(angular_p, omega_p)
1875
+ + wp.dot(angular_c, omega_c)
1876
+ )
1877
+
1878
+ err = 0.0
1879
+ compliance = linear_compliance
1880
+ damping = 0.0
1881
+ # consider joint limits irrespective of axis mode
1882
+ lower = axis_limits_lower[dim]
1883
+ upper = axis_limits_upper[dim]
1884
+ if e < lower:
1885
+ err = e - lower
1886
+ elif e > upper:
1887
+ err = e - upper
1888
+ else:
1889
+ target = axis_target[dim]
1890
+ if mode == JOINT_MODE_TARGET_POSITION:
1891
+ target = wp.clamp(target, lower, upper)
1892
+ if axis_stiffness[dim] > 0.0:
1893
+ err = e - target
1894
+ compliance = 1.0 / axis_stiffness[dim]
1895
+ damping = axis_damping[dim]
1896
+ elif mode == JOINT_MODE_TARGET_VELOCITY:
1897
+ if axis_stiffness[dim] > 0.0:
1898
+ err = (derr - target) * dt
1899
+ compliance = 1.0 / axis_stiffness[dim]
1900
+ damping = axis_damping[dim]
1901
+ derr = 0.0
1902
+
1903
+ if wp.abs(err) > 1e-9:
1904
+ lambda_in = 0.0
1905
+ d_lambda = compute_positional_correction(
1906
+ err,
1907
+ derr,
1908
+ pose_p,
1909
+ pose_c,
1910
+ m_inv_p,
1911
+ m_inv_c,
1912
+ I_inv_p,
1913
+ I_inv_c,
1914
+ linear_p,
1915
+ linear_c,
1916
+ angular_p,
1917
+ angular_c,
1918
+ lambda_in,
1919
+ compliance,
1920
+ damping,
1921
+ dt,
1922
+ )
1923
+
1924
+ lin_delta_p += linear_p * (d_lambda * linear_relaxation)
1925
+ ang_delta_p += angular_p * (d_lambda * angular_relaxation)
1926
+ lin_delta_c += linear_c * (d_lambda * linear_relaxation)
1927
+ ang_delta_c += angular_c * (d_lambda * angular_relaxation)
1928
+
1929
+ if (
1930
+ type == wp.sim.JOINT_FIXED
1931
+ or type == wp.sim.JOINT_PRISMATIC
1932
+ or type == wp.sim.JOINT_REVOLUTE
1933
+ or type == wp.sim.JOINT_UNIVERSAL
1934
+ or type == wp.sim.JOINT_COMPOUND
1935
+ or type == wp.sim.JOINT_D6
1936
+ ):
1937
+ # handle angular constraints
1938
+
1939
+ # local joint rotations
1940
+ q_p = wp.transform_get_rotation(X_wp)
1941
+ q_c = wp.transform_get_rotation(X_wc)
1942
+
1943
+ # make quats lie in same hemisphere
1944
+ if wp.dot(q_p, q_c) < 0.0:
1945
+ q_c *= -1.0
1946
+
1947
+ rel_q = wp.quat_inverse(q_p) * q_c
1948
+
1949
+ qtwist = wp.normalize(wp.quat(rel_q[0], 0.0, 0.0, rel_q[3]))
1950
+ qswing = rel_q * wp.quat_inverse(qtwist)
1951
+
1952
+ # decompose to a compound rotation each axis
1953
+ s = wp.sqrt(rel_q[0] * rel_q[0] + rel_q[3] * rel_q[3])
1954
+ invs = 1.0 / s
1955
+ invscube = invs * invs * invs
1956
+
1957
+ # handle axis-angle joints
1958
+
1959
+ # rescale twist from quaternion space to angular
1960
+ err_0 = 2.0 * wp.asin(wp.clamp(qtwist[0], -1.0, 1.0))
1961
+ err_1 = qswing[1]
1962
+ err_2 = qswing[2]
1963
+ # analytic gradients of swing-twist decomposition
1964
+ grad_0 = wp.quat(invs - rel_q[0] * rel_q[0] * invscube, 0.0, 0.0, -(rel_q[3] * rel_q[0]) * invscube)
1965
+ grad_1 = wp.quat(
1966
+ -rel_q[3] * (rel_q[3] * rel_q[2] + rel_q[0] * rel_q[1]) * invscube,
1967
+ rel_q[3] * invs,
1968
+ -rel_q[0] * invs,
1969
+ rel_q[0] * (rel_q[3] * rel_q[2] + rel_q[0] * rel_q[1]) * invscube,
1970
+ )
1971
+ grad_2 = wp.quat(
1972
+ rel_q[3] * (rel_q[3] * rel_q[1] - rel_q[0] * rel_q[2]) * invscube,
1973
+ rel_q[0] * invs,
1974
+ rel_q[3] * invs,
1975
+ rel_q[0] * (rel_q[2] * rel_q[0] - rel_q[3] * rel_q[1]) * invscube,
1976
+ )
1977
+ grad_0 *= 2.0 / wp.abs(qtwist[3])
1978
+ # grad_0 *= 2.0 / wp.sqrt(1.0-qtwist[0]*qtwist[0]) # derivative of asin(x) = 1/sqrt(1-x^2)
1979
+
1980
+ # rescale swing
1981
+ swing_sq = qswing[3] * qswing[3]
1982
+ # if swing axis magnitude close to zero vector, just treat in quaternion space
1983
+ angularEps = 1.0e-4
1984
+ if swing_sq + angularEps < 1.0:
1985
+ d = wp.sqrt(1.0 - qswing[3] * qswing[3])
1986
+ theta = 2.0 * wp.acos(wp.clamp(qswing[3], -1.0, 1.0))
1987
+ scale = theta / d
1988
+
1989
+ err_1 *= scale
1990
+ err_2 *= scale
1991
+
1992
+ grad_1 *= scale
1993
+ grad_2 *= scale
1994
+
1995
+ errs = wp.vec3(err_0, err_1, err_2)
1996
+ grad_x = wp.vec3(grad_0[0], grad_1[0], grad_2[0])
1997
+ grad_y = wp.vec3(grad_0[1], grad_1[1], grad_2[1])
1998
+ grad_z = wp.vec3(grad_0[2], grad_1[2], grad_2[2])
1999
+ grad_w = wp.vec3(grad_0[3], grad_1[3], grad_2[3])
2000
+
2001
+ # compute joint target, stiffness, damping
2002
+ ke_sum = float(0.0)
2003
+ axis_limits = wp.spatial_vector(0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2004
+ axis_mode = wp.vec3i(0, 0, 0)
2005
+ axis_target_ke_kd = wp.mat33(0.0)
2006
+ # avoid a for loop here since local variables would need to be modified which is not yet differentiable
2007
+ if ang_axis_count > 0:
2008
+ axis_idx = axis_start + lin_axis_count
2009
+ axis = joint_axis[axis_idx]
2010
+ lo_temp = axis * joint_limit_lower[axis_idx]
2011
+ up_temp = axis * joint_limit_upper[axis_idx]
2012
+ axis_limits = wp.spatial_vector(vec_min(lo_temp, up_temp), vec_max(lo_temp, up_temp))
2013
+ mode = joint_axis_mode[axis_idx]
2014
+ if mode != JOINT_MODE_FORCE: # position or velocity target
2015
+ ke = joint_target_ke[axis_idx]
2016
+ kd = joint_target_kd[axis_idx]
2017
+ target = joint_act[axis_idx]
2018
+ axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
2019
+ axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
2020
+ ke_sum += ke
2021
+ if ang_axis_count > 1:
2022
+ axis_idx = axis_start + lin_axis_count + 1
2023
+ axis = joint_axis[axis_idx]
2024
+ lower = joint_limit_lower[axis_idx]
2025
+ upper = joint_limit_upper[axis_idx]
2026
+ axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
2027
+ mode = joint_axis_mode[axis_idx]
2028
+ if mode != JOINT_MODE_FORCE: # position or velocity target
2029
+ ke = joint_target_ke[axis_idx]
2030
+ kd = joint_target_kd[axis_idx]
2031
+ target = joint_act[axis_idx]
2032
+ axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
2033
+ axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
2034
+ ke_sum += ke
2035
+ if ang_axis_count > 2:
2036
+ axis_idx = axis_start + lin_axis_count + 2
2037
+ axis = joint_axis[axis_idx]
2038
+ lower = joint_limit_lower[axis_idx]
2039
+ upper = joint_limit_upper[axis_idx]
2040
+ axis_limits = update_joint_axis_limits(axis, lower, upper, axis_limits)
2041
+ mode = joint_axis_mode[axis_idx]
2042
+ if mode != JOINT_MODE_FORCE: # position or velocity target
2043
+ ke = joint_target_ke[axis_idx]
2044
+ kd = joint_target_kd[axis_idx]
2045
+ target = joint_act[axis_idx]
2046
+ axis_mode = update_joint_axis_mode(mode, axis, axis_mode)
2047
+ axis_target_ke_kd = update_joint_axis_target_ke_kd(axis, target, ke, kd, axis_target_ke_kd)
2048
+ ke_sum += ke
2049
+
2050
+ axis_target = axis_target_ke_kd[0]
2051
+ axis_stiffness = axis_target_ke_kd[1]
2052
+ axis_damping = axis_target_ke_kd[2]
2053
+ if ke_sum > 0.0:
2054
+ axis_target /= ke_sum
2055
+ axis_limits_lower = wp.spatial_top(axis_limits)
2056
+ axis_limits_upper = wp.spatial_bottom(axis_limits)
2057
+
2058
+ # if type == wp.sim.JOINT_D6:
2059
+ # wp.printf("axis_target: %f %f %f\t axis_stiffness: %f %f %f\t axis_damping: %f %f %f\t axis_limits_lower: %f %f %f \t axis_limits_upper: %f %f %f\n",
2060
+ # axis_target[0], axis_target[1], axis_target[2],
2061
+ # axis_stiffness[0], axis_stiffness[1], axis_stiffness[2],
2062
+ # axis_damping[0], axis_damping[1], axis_damping[2],
2063
+ # axis_limits_lower[0], axis_limits_lower[1], axis_limits_lower[2],
2064
+ # axis_limits_upper[0], axis_limits_upper[1], axis_limits_upper[2])
2065
+ # # wp.printf("wp.sqrt(1.0-qtwist[0]*qtwist[0]) = %f\n", wp.sqrt(1.0-qtwist[0]*qtwist[0]))
2066
+
2067
+ for dim in range(3):
2068
+ e = errs[dim]
2069
+ mode = axis_mode[dim]
2070
+
2071
+ # analytic gradients of swing-twist decomposition
2072
+ grad = wp.quat(grad_x[dim], grad_y[dim], grad_z[dim], grad_w[dim])
2073
+
2074
+ quat_c = 0.5 * q_p * grad * wp.quat_inverse(q_c)
2075
+ angular_c = wp.vec3(quat_c[0], quat_c[1], quat_c[2])
2076
+ angular_p = -angular_c
2077
+ # time derivative of the constraint
2078
+ derr = wp.dot(angular_p, omega_p) + wp.dot(angular_c, omega_c)
2079
+
2080
+ err = 0.0
2081
+ compliance = angular_compliance
2082
+ damping = 0.0
2083
+
2084
+ # consider joint limits irrespective of mode
2085
+ lower = axis_limits_lower[dim]
2086
+ upper = axis_limits_upper[dim]
2087
+ if e < lower:
2088
+ err = e - lower
2089
+ elif e > upper:
2090
+ err = e - upper
2091
+ else:
2092
+ target = axis_target[dim]
2093
+ if mode == JOINT_MODE_TARGET_POSITION:
2094
+ target = wp.clamp(target, lower, upper)
2095
+ if axis_stiffness[dim] > 0.0:
2096
+ err = e - target
2097
+ compliance = 1.0 / axis_stiffness[dim]
2098
+ damping = axis_damping[dim]
2099
+ elif mode == JOINT_MODE_TARGET_VELOCITY:
2100
+ if axis_stiffness[dim] > 0.0:
2101
+ err = (derr - target) * dt
2102
+ compliance = 1.0 / axis_stiffness[dim]
2103
+ damping = axis_damping[dim]
2104
+ derr = 0.0
2105
+
2106
+ d_lambda = (
2107
+ compute_angular_correction(
2108
+ err, derr, pose_p, pose_c, I_inv_p, I_inv_c, angular_p, angular_c, 0.0, compliance, damping, dt
2109
+ )
2110
+ * angular_relaxation
2111
+ )
2112
+
2113
+ # update deltas
2114
+ ang_delta_p += angular_p * d_lambda
2115
+ ang_delta_c += angular_c * d_lambda
2116
+
2117
+ if id_p >= 0:
2118
+ wp.atomic_add(deltas, id_p, wp.spatial_vector(ang_delta_p, lin_delta_p))
2119
+ if id_c >= 0:
2120
+ wp.atomic_add(deltas, id_c, wp.spatial_vector(ang_delta_c, lin_delta_c))
2121
+
2122
+
2123
+ @wp.func
2124
+ def compute_contact_constraint_delta(
2125
+ err: float,
2126
+ tf_a: wp.transform,
2127
+ tf_b: wp.transform,
2128
+ m_inv_a: float,
2129
+ m_inv_b: float,
2130
+ I_inv_a: wp.mat33,
2131
+ I_inv_b: wp.mat33,
2132
+ linear_a: wp.vec3,
2133
+ linear_b: wp.vec3,
2134
+ angular_a: wp.vec3,
2135
+ angular_b: wp.vec3,
2136
+ relaxation: float,
2137
+ dt: float,
2138
+ ) -> float:
2139
+ denom = 0.0
2140
+ denom += wp.length_sq(linear_a) * m_inv_a
2141
+ denom += wp.length_sq(linear_b) * m_inv_b
2142
+
2143
+ q1 = wp.transform_get_rotation(tf_a)
2144
+ q2 = wp.transform_get_rotation(tf_b)
2145
+
2146
+ # Eq. 2-3 (make sure to project into the frame of the body)
2147
+ rot_angular_a = wp.quat_rotate_inv(q1, angular_a)
2148
+ rot_angular_b = wp.quat_rotate_inv(q2, angular_b)
2149
+
2150
+ denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
2151
+ denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
2152
+
2153
+ delta_lambda = -err
2154
+ if denom > 0.0:
2155
+ delta_lambda /= dt * denom
2156
+
2157
+ return delta_lambda * relaxation
2158
+
2159
+
2160
+ @wp.func
2161
+ def compute_positional_correction(
2162
+ err: float,
2163
+ derr: float,
2164
+ tf_a: wp.transform,
2165
+ tf_b: wp.transform,
2166
+ m_inv_a: float,
2167
+ m_inv_b: float,
2168
+ I_inv_a: wp.mat33,
2169
+ I_inv_b: wp.mat33,
2170
+ linear_a: wp.vec3,
2171
+ linear_b: wp.vec3,
2172
+ angular_a: wp.vec3,
2173
+ angular_b: wp.vec3,
2174
+ lambda_in: float,
2175
+ compliance: float,
2176
+ damping: float,
2177
+ dt: float,
2178
+ ) -> float:
2179
+ denom = 0.0
2180
+ denom += wp.length_sq(linear_a) * m_inv_a
2181
+ denom += wp.length_sq(linear_b) * m_inv_b
2182
+
2183
+ q1 = wp.transform_get_rotation(tf_a)
2184
+ q2 = wp.transform_get_rotation(tf_b)
2185
+
2186
+ # Eq. 2-3 (make sure to project into the frame of the body)
2187
+ rot_angular_a = wp.quat_rotate_inv(q1, angular_a)
2188
+ rot_angular_b = wp.quat_rotate_inv(q2, angular_b)
2189
+
2190
+ denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
2191
+ denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
2192
+
2193
+ alpha = compliance
2194
+ gamma = compliance * damping
2195
+
2196
+ delta_lambda = -(err + alpha * lambda_in + gamma * derr)
2197
+ if denom + alpha > 0.0:
2198
+ delta_lambda /= (dt + gamma) * denom + alpha / dt
2199
+
2200
+ return delta_lambda
2201
+
2202
+
2203
+ @wp.func
2204
+ def compute_angular_correction(
2205
+ err: float,
2206
+ derr: float,
2207
+ tf_a: wp.transform,
2208
+ tf_b: wp.transform,
2209
+ I_inv_a: wp.mat33,
2210
+ I_inv_b: wp.mat33,
2211
+ angular_a: wp.vec3,
2212
+ angular_b: wp.vec3,
2213
+ lambda_in: float,
2214
+ compliance: float,
2215
+ damping: float,
2216
+ dt: float,
2217
+ ) -> float:
2218
+ denom = 0.0
2219
+
2220
+ q1 = wp.transform_get_rotation(tf_a)
2221
+ q2 = wp.transform_get_rotation(tf_b)
2222
+
2223
+ # Eq. 2-3 (make sure to project into the frame of the body)
2224
+ rot_angular_a = wp.quat_rotate_inv(q1, angular_a)
2225
+ rot_angular_b = wp.quat_rotate_inv(q2, angular_b)
2226
+
2227
+ denom += wp.dot(rot_angular_a, I_inv_a * rot_angular_a)
2228
+ denom += wp.dot(rot_angular_b, I_inv_b * rot_angular_b)
2229
+
2230
+ alpha = compliance
2231
+ gamma = compliance * damping
2232
+
2233
+ delta_lambda = -(err + alpha * lambda_in + gamma * derr)
2234
+ if denom + alpha > 0.0:
2235
+ delta_lambda /= (dt + gamma) * denom + alpha / dt
2236
+
2237
+ return delta_lambda
2238
+
2239
+
2240
+ @wp.kernel
2241
+ def solve_body_contact_positions(
2242
+ body_q: wp.array(dtype=wp.transform),
2243
+ body_qd: wp.array(dtype=wp.spatial_vector),
2244
+ body_com: wp.array(dtype=wp.vec3),
2245
+ body_m_inv: wp.array(dtype=float),
2246
+ body_I_inv: wp.array(dtype=wp.mat33),
2247
+ shape_body: wp.array(dtype=int),
2248
+ contact_count: wp.array(dtype=int),
2249
+ contact_point0: wp.array(dtype=wp.vec3),
2250
+ contact_point1: wp.array(dtype=wp.vec3),
2251
+ contact_offset0: wp.array(dtype=wp.vec3),
2252
+ contact_offset1: wp.array(dtype=wp.vec3),
2253
+ contact_normal: wp.array(dtype=wp.vec3),
2254
+ contact_thickness: wp.array(dtype=float),
2255
+ contact_shape0: wp.array(dtype=int),
2256
+ contact_shape1: wp.array(dtype=int),
2257
+ shape_materials: ModelShapeMaterials,
2258
+ relaxation: float,
2259
+ dt: float,
2260
+ contact_torsional_friction: float,
2261
+ contact_rolling_friction: float,
2262
+ # outputs
2263
+ deltas: wp.array(dtype=wp.spatial_vector),
2264
+ contact_inv_weight: wp.array(dtype=float),
2265
+ ):
2266
+ tid = wp.tid()
2267
+
2268
+ count = contact_count[0]
2269
+ if tid >= count:
2270
+ return
2271
+
2272
+ shape_a = contact_shape0[tid]
2273
+ shape_b = contact_shape1[tid]
2274
+ if shape_a == shape_b:
2275
+ return
2276
+ body_a = -1
2277
+ if shape_a >= 0:
2278
+ body_a = shape_body[shape_a]
2279
+ body_b = -1
2280
+ if shape_b >= 0:
2281
+ body_b = shape_body[shape_b]
2282
+ if body_a == body_b:
2283
+ return
2284
+
2285
+ # find body to world transform
2286
+ X_wb_a = wp.transform_identity()
2287
+ X_wb_b = wp.transform_identity()
2288
+ if body_a >= 0:
2289
+ X_wb_a = body_q[body_a]
2290
+ if body_b >= 0:
2291
+ X_wb_b = body_q[body_b]
2292
+
2293
+ # compute body position in world space
2294
+ bx_a = wp.transform_point(X_wb_a, contact_point0[tid])
2295
+ bx_b = wp.transform_point(X_wb_b, contact_point1[tid])
2296
+
2297
+ thickness = contact_thickness[tid]
2298
+ n = -contact_normal[tid]
2299
+ d = wp.dot(n, bx_b - bx_a) - thickness
2300
+
2301
+ if d >= 0.0:
2302
+ return
2303
+
2304
+ m_inv_a = 0.0
2305
+ m_inv_b = 0.0
2306
+ I_inv_a = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2307
+ I_inv_b = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2308
+ # center of mass in body frame
2309
+ com_a = wp.vec3(0.0)
2310
+ com_b = wp.vec3(0.0)
2311
+ # body to world transform
2312
+ X_wb_a = wp.transform_identity()
2313
+ X_wb_b = wp.transform_identity()
2314
+ # angular velocities
2315
+ omega_a = wp.vec3(0.0)
2316
+ omega_b = wp.vec3(0.0)
2317
+ # contact offset in body frame
2318
+ offset_a = contact_offset0[tid]
2319
+ offset_b = contact_offset1[tid]
2320
+
2321
+ if body_a >= 0:
2322
+ X_wb_a = body_q[body_a]
2323
+ com_a = body_com[body_a]
2324
+ m_inv_a = body_m_inv[body_a]
2325
+ I_inv_a = body_I_inv[body_a]
2326
+ omega_a = wp.spatial_top(body_qd[body_a])
2327
+
2328
+ if body_b >= 0:
2329
+ X_wb_b = body_q[body_b]
2330
+ com_b = body_com[body_b]
2331
+ m_inv_b = body_m_inv[body_b]
2332
+ I_inv_b = body_I_inv[body_b]
2333
+ omega_b = wp.spatial_top(body_qd[body_b])
2334
+
2335
+ # use average contact material properties
2336
+ mat_nonzero = 0
2337
+ mu = 0.0
2338
+ if shape_a >= 0:
2339
+ mat_nonzero += 1
2340
+ mu += shape_materials.mu[shape_a]
2341
+ if shape_b >= 0:
2342
+ mat_nonzero += 1
2343
+ mu += shape_materials.mu[shape_b]
2344
+ if mat_nonzero > 0:
2345
+ mu /= float(mat_nonzero)
2346
+
2347
+ r_a = bx_a - wp.transform_point(X_wb_a, com_a)
2348
+ r_b = bx_b - wp.transform_point(X_wb_b, com_b)
2349
+
2350
+ angular_a = -wp.cross(r_a, n)
2351
+ angular_b = wp.cross(r_b, n)
2352
+
2353
+ if contact_inv_weight:
2354
+ if body_a >= 0:
2355
+ wp.atomic_add(contact_inv_weight, body_a, 1.0)
2356
+ if body_b >= 0:
2357
+ wp.atomic_add(contact_inv_weight, body_b, 1.0)
2358
+
2359
+ lambda_n = compute_contact_constraint_delta(
2360
+ d, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, -n, n, angular_a, angular_b, relaxation, dt
2361
+ )
2362
+
2363
+ lin_delta_a = -n * lambda_n
2364
+ lin_delta_b = n * lambda_n
2365
+ ang_delta_a = angular_a * lambda_n
2366
+ ang_delta_b = angular_b * lambda_n
2367
+
2368
+ # linear friction
2369
+ if mu > 0.0:
2370
+ # add on displacement from surface offsets, this ensures we include any rotational effects due to thickness from feature
2371
+ # need to use the current rotation to account for friction due to angular effects (e.g.: slipping contact)
2372
+ bx_a += wp.transform_vector(X_wb_a, offset_a)
2373
+ bx_b += wp.transform_vector(X_wb_b, offset_b)
2374
+
2375
+ # update delta
2376
+ delta = bx_b - bx_a
2377
+ friction_delta = delta - wp.dot(n, delta) * n
2378
+
2379
+ perp = wp.normalize(friction_delta)
2380
+
2381
+ r_a = bx_a - wp.transform_point(X_wb_a, com_a)
2382
+ r_b = bx_b - wp.transform_point(X_wb_b, com_b)
2383
+
2384
+ angular_a = -wp.cross(r_a, perp)
2385
+ angular_b = wp.cross(r_b, perp)
2386
+
2387
+ err = wp.length(friction_delta)
2388
+
2389
+ if err > 0.0:
2390
+ lambda_fr = compute_contact_constraint_delta(
2391
+ err, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, -perp, perp, angular_a, angular_b, 1.0, dt
2392
+ )
2393
+
2394
+ # limit friction based on incremental normal force, good approximation to limiting on total force
2395
+ lambda_fr = wp.max(lambda_fr, -lambda_n * mu)
2396
+
2397
+ lin_delta_a -= perp * lambda_fr
2398
+ lin_delta_b += perp * lambda_fr
2399
+
2400
+ ang_delta_a += angular_a * lambda_fr
2401
+ ang_delta_b += angular_b * lambda_fr
2402
+
2403
+ torsional_friction = mu * contact_torsional_friction
2404
+
2405
+ delta_omega = omega_b - omega_a
2406
+
2407
+ if torsional_friction > 0.0:
2408
+ err = wp.dot(delta_omega, n) * dt
2409
+
2410
+ if wp.abs(err) > 0.0:
2411
+ lin = wp.vec3(0.0)
2412
+ lambda_torsion = compute_contact_constraint_delta(
2413
+ err, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, lin, lin, -n, n, 1.0, dt
2414
+ )
2415
+
2416
+ lambda_torsion = wp.clamp(lambda_torsion, -lambda_n * torsional_friction, lambda_n * torsional_friction)
2417
+
2418
+ ang_delta_a -= n * lambda_torsion
2419
+ ang_delta_b += n * lambda_torsion
2420
+
2421
+ rolling_friction = mu * contact_rolling_friction
2422
+ if rolling_friction > 0.0:
2423
+ delta_omega -= wp.dot(n, delta_omega) * n
2424
+ err = wp.length(delta_omega) * dt
2425
+ if err > 0.0:
2426
+ lin = wp.vec3(0.0)
2427
+ roll_n = wp.normalize(delta_omega)
2428
+ lambda_roll = compute_contact_constraint_delta(
2429
+ err, X_wb_a, X_wb_b, m_inv_a, m_inv_b, I_inv_a, I_inv_b, lin, lin, -roll_n, roll_n, 1.0, dt
2430
+ )
2431
+
2432
+ lambda_roll = wp.max(lambda_roll, -lambda_n * rolling_friction)
2433
+
2434
+ ang_delta_a -= roll_n * lambda_roll
2435
+ ang_delta_b += roll_n * lambda_roll
2436
+
2437
+ if body_a >= 0:
2438
+ wp.atomic_add(deltas, body_a, wp.spatial_vector(ang_delta_a, lin_delta_a))
2439
+ if body_b >= 0:
2440
+ wp.atomic_add(deltas, body_b, wp.spatial_vector(ang_delta_b, lin_delta_b))
2441
+
2442
+
2443
+ @wp.kernel
2444
+ def update_body_velocities(
2445
+ poses: wp.array(dtype=wp.transform),
2446
+ poses_prev: wp.array(dtype=wp.transform),
2447
+ body_com: wp.array(dtype=wp.vec3),
2448
+ dt: float,
2449
+ qd_out: wp.array(dtype=wp.spatial_vector),
2450
+ ):
2451
+ tid = wp.tid()
2452
+
2453
+ pose = poses[tid]
2454
+ pose_prev = poses_prev[tid]
2455
+
2456
+ x = wp.transform_get_translation(pose)
2457
+ x_prev = wp.transform_get_translation(pose_prev)
2458
+
2459
+ q = wp.transform_get_rotation(pose)
2460
+ q_prev = wp.transform_get_rotation(pose_prev)
2461
+
2462
+ # Update body velocities according to Alg. 2
2463
+ # XXX we consider the body COM as the origin of the body frame
2464
+ x_com = x + wp.quat_rotate(q, body_com[tid])
2465
+ x_com_prev = x_prev + wp.quat_rotate(q_prev, body_com[tid])
2466
+
2467
+ # XXX consider the velocity of the COM
2468
+ v = (x_com - x_com_prev) / dt
2469
+ dq = q * wp.quat_inverse(q_prev)
2470
+
2471
+ omega = 2.0 / dt * wp.vec3(dq[0], dq[1], dq[2])
2472
+ if dq[3] < 0.0:
2473
+ omega = -omega
2474
+
2475
+ qd_out[tid] = wp.spatial_vector(omega, v)
2476
+
2477
+
2478
+ @wp.kernel
2479
+ def apply_rigid_restitution(
2480
+ body_q: wp.array(dtype=wp.transform),
2481
+ body_qd: wp.array(dtype=wp.spatial_vector),
2482
+ body_q_prev: wp.array(dtype=wp.transform),
2483
+ body_qd_prev: wp.array(dtype=wp.spatial_vector),
2484
+ body_com: wp.array(dtype=wp.vec3),
2485
+ body_m_inv: wp.array(dtype=float),
2486
+ body_I_inv: wp.array(dtype=wp.mat33),
2487
+ shape_body: wp.array(dtype=int),
2488
+ contact_count: wp.array(dtype=int),
2489
+ contact_normal: wp.array(dtype=wp.vec3),
2490
+ contact_shape0: wp.array(dtype=int),
2491
+ contact_shape1: wp.array(dtype=int),
2492
+ shape_materials: ModelShapeMaterials,
2493
+ contact_point0: wp.array(dtype=wp.vec3),
2494
+ contact_point1: wp.array(dtype=wp.vec3),
2495
+ contact_offset0: wp.array(dtype=wp.vec3),
2496
+ contact_offset1: wp.array(dtype=wp.vec3),
2497
+ contact_thickness: wp.array(dtype=float),
2498
+ contact_inv_weight: wp.array(dtype=float),
2499
+ gravity: wp.vec3,
2500
+ dt: float,
2501
+ # outputs
2502
+ deltas: wp.array(dtype=wp.spatial_vector),
2503
+ ):
2504
+ tid = wp.tid()
2505
+
2506
+ count = contact_count[0]
2507
+ if tid >= count:
2508
+ return
2509
+ shape_a = contact_shape0[tid]
2510
+ shape_b = contact_shape1[tid]
2511
+ if shape_a == shape_b:
2512
+ return
2513
+ body_a = -1
2514
+ body_b = -1
2515
+
2516
+ # use average contact material properties
2517
+ mat_nonzero = 0
2518
+ restitution = 0.0
2519
+ if shape_a >= 0:
2520
+ mat_nonzero += 1
2521
+ restitution += shape_materials.restitution[shape_a]
2522
+ body_a = shape_body[shape_a]
2523
+ if shape_b >= 0:
2524
+ mat_nonzero += 1
2525
+ restitution += shape_materials.restitution[shape_b]
2526
+ body_b = shape_body[shape_b]
2527
+ if mat_nonzero > 0:
2528
+ restitution /= float(mat_nonzero)
2529
+ if body_a == body_b:
2530
+ return
2531
+
2532
+ m_inv_a = 0.0
2533
+ m_inv_b = 0.0
2534
+ I_inv_a = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2535
+ I_inv_b = wp.mat33(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
2536
+ # body to world transform
2537
+ X_wb_a_prev = wp.transform_identity()
2538
+ X_wb_b_prev = wp.transform_identity()
2539
+ # center of mass in body frame
2540
+ com_a = wp.vec3(0.0)
2541
+ com_b = wp.vec3(0.0)
2542
+ # previous velocity at contact points
2543
+ v_a = wp.vec3(0.0)
2544
+ v_b = wp.vec3(0.0)
2545
+ # new velocity at contact points
2546
+ v_a_new = wp.vec3(0.0)
2547
+ v_b_new = wp.vec3(0.0)
2548
+ # inverse mass used to compute the impulse
2549
+ inv_mass = 0.0
2550
+
2551
+ if body_a >= 0:
2552
+ X_wb_a_prev = body_q_prev[body_a]
2553
+ # X_wb_a = body_q[body_a]
2554
+ m_inv_a = body_m_inv[body_a]
2555
+ I_inv_a = body_I_inv[body_a]
2556
+ com_a = body_com[body_a]
2557
+
2558
+ if body_b >= 0:
2559
+ X_wb_b_prev = body_q_prev[body_b]
2560
+ # X_wb_b = body_q[body_b]
2561
+ m_inv_b = body_m_inv[body_b]
2562
+ I_inv_b = body_I_inv[body_b]
2563
+ com_b = body_com[body_b]
2564
+
2565
+ # compute body position in world space
2566
+ bx_a = wp.transform_point(X_wb_a_prev, contact_point0[tid] + contact_offset0[tid])
2567
+ bx_b = wp.transform_point(X_wb_b_prev, contact_point1[tid] + contact_offset1[tid])
2568
+
2569
+ thickness = contact_thickness[tid]
2570
+ n = contact_normal[tid]
2571
+ d = -wp.dot(n, bx_b - bx_a) - thickness
2572
+ if d >= 0.0:
2573
+ return
2574
+
2575
+ r_a = bx_a - wp.transform_point(X_wb_a_prev, com_a)
2576
+ r_b = bx_b - wp.transform_point(X_wb_b_prev, com_b)
2577
+
2578
+ rxn_a = wp.vec3(0.0)
2579
+ rxn_b = wp.vec3(0.0)
2580
+ if body_a >= 0:
2581
+ v_a = velocity_at_point(body_qd_prev[body_a], r_a) + gravity * dt
2582
+ v_a_new = velocity_at_point(body_qd[body_a], r_a)
2583
+ q_a = wp.transform_get_rotation(X_wb_a_prev)
2584
+ rxn_a = wp.quat_rotate_inv(q_a, wp.cross(r_a, n))
2585
+ # Eq. 2
2586
+ inv_mass_a = m_inv_a + wp.dot(rxn_a, I_inv_a * rxn_a)
2587
+ # if contact_inv_weight:
2588
+ # if contact_inv_weight[body_a] > 0.0:
2589
+ # inv_mass_a *= contact_inv_weight[body_a]
2590
+ inv_mass += inv_mass_a
2591
+ if body_b >= 0:
2592
+ v_b = velocity_at_point(body_qd_prev[body_b], r_b) + gravity * dt
2593
+ v_b_new = velocity_at_point(body_qd[body_b], r_b)
2594
+ q_b = wp.transform_get_rotation(X_wb_b_prev)
2595
+ rxn_b = wp.quat_rotate_inv(q_b, wp.cross(r_b, n))
2596
+ # Eq. 3
2597
+ inv_mass_b = m_inv_b + wp.dot(rxn_b, I_inv_b * rxn_b)
2598
+ # if contact_inv_weight:
2599
+ # if contact_inv_weight[body_b] > 0.0:
2600
+ # inv_mass_b *= contact_inv_weight[body_b]
2601
+ inv_mass += inv_mass_b
2602
+
2603
+ if inv_mass == 0.0:
2604
+ return
2605
+
2606
+ # Eq. 29
2607
+ rel_vel_old = wp.dot(n, v_a - v_b)
2608
+ rel_vel_new = wp.dot(n, v_a_new - v_b_new)
2609
+
2610
+ if rel_vel_old >= 0.0:
2611
+ return
2612
+
2613
+ # Eq. 34
2614
+ dv = (-rel_vel_new - restitution * rel_vel_old) / inv_mass
2615
+
2616
+ # Eq. 33
2617
+ if body_a >= 0:
2618
+ dv_a = dv
2619
+ # if contact_inv_weight:
2620
+ # if contact_inv_weight[body_a] > 0.0:
2621
+ # dv_a *= contact_inv_weight[body_a]
2622
+ q_a = wp.transform_get_rotation(X_wb_a_prev)
2623
+ dq = wp.quat_rotate(q_a, I_inv_a * rxn_a * dv_a)
2624
+ wp.atomic_add(deltas, body_a, wp.spatial_vector(dq, n * m_inv_a * dv_a))
2625
+
2626
+ if body_b >= 0:
2627
+ dv_b = -dv
2628
+ # if contact_inv_weight:
2629
+ # if contact_inv_weight[body_b] > 0.0:
2630
+ # dv_b *= contact_inv_weight[body_b]
2631
+ q_b = wp.transform_get_rotation(X_wb_b_prev)
2632
+ dq = wp.quat_rotate(q_b, I_inv_b * rxn_b * dv_b)
2633
+ wp.atomic_add(deltas, body_b, wp.spatial_vector(dq, n * m_inv_b * dv_b))
2634
+
2635
+
2636
+ class XPBDIntegrator(Integrator):
2637
+ """An implicit integrator using eXtended Position-Based Dynamics (XPBD) for rigid and soft body simulation.
2638
+
2639
+ References:
2640
+ - Miles Macklin, Matthias Müller, and Nuttapong Chentanez. 2016. XPBD: position-based simulation of compliant constrained dynamics. In Proceedings of the 9th International Conference on Motion in Games (MIG '16). Association for Computing Machinery, New York, NY, USA, 49-54. https://doi.org/10.1145/2994258.2994272
2641
+ - Matthias Müller, Miles Macklin, Nuttapong Chentanez, Stefan Jeschke, and Tae-Yong Kim. 2020. Detailed rigid body simulation with extended position based dynamics. In Proceedings of the ACM SIGGRAPH/Eurographics Symposium on Computer Animation (SCA '20). Eurographics Association, Goslar, DEU, Article 10, 1-12. https://doi.org/10.1111/cgf.14105
2642
+
2643
+ After constructing :class:`Model`, :class:`State`, and :class:`Control` (optional) objects, this time-integrator
2644
+ may be used to advance the simulation state forward in time.
2645
+
2646
+ Example
2647
+ -------
2648
+
2649
+ .. code-block:: python
2650
+
2651
+ integrator = wp.XPBDIntegrator()
2652
+
2653
+ # simulation loop
2654
+ for i in range(100):
2655
+ state = integrator.simulate(model, state_in, state_out, dt, control)
2656
+
2657
+ """
2658
+
2659
+ def __init__(
2660
+ self,
2661
+ iterations=2,
2662
+ soft_body_relaxation=0.9,
2663
+ soft_contact_relaxation=0.9,
2664
+ joint_linear_relaxation=0.7,
2665
+ joint_angular_relaxation=0.4,
2666
+ rigid_contact_relaxation=0.8,
2667
+ rigid_contact_con_weighting=True,
2668
+ angular_damping=0.0,
2669
+ enable_restitution=False,
2670
+ ):
2671
+ self.iterations = iterations
2672
+
2673
+ self.soft_body_relaxation = soft_body_relaxation
2674
+ self.soft_contact_relaxation = soft_contact_relaxation
2675
+
2676
+ self.joint_linear_relaxation = joint_linear_relaxation
2677
+ self.joint_angular_relaxation = joint_angular_relaxation
2678
+
2679
+ self.rigid_contact_relaxation = rigid_contact_relaxation
2680
+ self.rigid_contact_con_weighting = rigid_contact_con_weighting
2681
+
2682
+ self.angular_damping = angular_damping
2683
+
2684
+ self.enable_restitution = enable_restitution
2685
+
2686
+ self.compute_body_velocity_from_position_delta = False
2687
+
2688
+ # helper variables to track constraint resolution vars
2689
+ self._particle_delta_counter = 0
2690
+ self._body_delta_counter = 0
2691
+
2692
+ def apply_particle_deltas(
2693
+ self,
2694
+ model: Model,
2695
+ state_in: State,
2696
+ state_out: State,
2697
+ particle_deltas: wp.array,
2698
+ dt: float,
2699
+ ):
2700
+ if state_in.requires_grad:
2701
+ particle_q = state_out.particle_q
2702
+ # allocate new particle arrays so gradients can be tracked correctly without overwriting
2703
+ new_particle_q = wp.empty_like(state_out.particle_q)
2704
+ new_particle_qd = wp.empty_like(state_out.particle_qd)
2705
+ self._particle_delta_counter += 1
2706
+ else:
2707
+ if self._particle_delta_counter == 0:
2708
+ particle_q = state_out.particle_q
2709
+ new_particle_q = state_in.particle_q
2710
+ new_particle_qd = state_in.particle_qd
2711
+ else:
2712
+ particle_q = state_in.particle_q
2713
+ new_particle_q = state_out.particle_q
2714
+ new_particle_qd = state_out.particle_qd
2715
+ self._particle_delta_counter = 1 - self._particle_delta_counter
2716
+
2717
+ wp.launch(
2718
+ kernel=apply_particle_deltas,
2719
+ dim=model.particle_count,
2720
+ inputs=[
2721
+ self.particle_q_init,
2722
+ particle_q,
2723
+ model.particle_flags,
2724
+ particle_deltas,
2725
+ dt,
2726
+ model.particle_max_velocity,
2727
+ ],
2728
+ outputs=[new_particle_q, new_particle_qd],
2729
+ device=model.device,
2730
+ )
2731
+
2732
+ if state_in.requires_grad:
2733
+ state_out.particle_q = new_particle_q
2734
+ state_out.particle_qd = new_particle_qd
2735
+
2736
+ return new_particle_q, new_particle_qd
2737
+
2738
+ def apply_body_deltas(
2739
+ self,
2740
+ model: Model,
2741
+ state_in: State,
2742
+ state_out: State,
2743
+ body_deltas: wp.array,
2744
+ dt: float,
2745
+ rigid_contact_inv_weight: wp.array = None,
2746
+ ):
2747
+ with wp.ScopedTimer("apply_body_deltas", False):
2748
+ if state_in.requires_grad:
2749
+ body_q = state_out.body_q
2750
+ body_qd = state_out.body_qd
2751
+ new_body_q = wp.clone(body_q)
2752
+ new_body_qd = wp.clone(body_qd)
2753
+ self._body_delta_counter += 1
2754
+ else:
2755
+ if self._body_delta_counter == 0:
2756
+ body_q = state_out.body_q
2757
+ body_qd = state_out.body_qd
2758
+ new_body_q = state_in.body_q
2759
+ new_body_qd = state_in.body_qd
2760
+ else:
2761
+ body_q = state_in.body_q
2762
+ body_qd = state_in.body_qd
2763
+ new_body_q = state_out.body_q
2764
+ new_body_qd = state_out.body_qd
2765
+ self._body_delta_counter = 1 - self._body_delta_counter
2766
+
2767
+ wp.launch(
2768
+ kernel=apply_body_deltas,
2769
+ dim=model.body_count,
2770
+ inputs=[
2771
+ body_q,
2772
+ body_qd,
2773
+ model.body_com,
2774
+ model.body_inertia,
2775
+ model.body_inv_mass,
2776
+ model.body_inv_inertia,
2777
+ body_deltas,
2778
+ rigid_contact_inv_weight,
2779
+ dt,
2780
+ ],
2781
+ outputs=[
2782
+ new_body_q,
2783
+ new_body_qd,
2784
+ ],
2785
+ device=model.device,
2786
+ )
2787
+
2788
+ if state_in.requires_grad:
2789
+ state_out.body_q = new_body_q
2790
+ state_out.body_qd = new_body_qd
2791
+
2792
+ return new_body_q, new_body_qd
2793
+
2794
+ def simulate(self, model: Model, state_in: State, state_out: State, dt: float, control: Control = None):
2795
+ requires_grad = state_in.requires_grad
2796
+ self._particle_delta_counter = 0
2797
+ self._body_delta_counter = 0
2798
+
2799
+ particle_q = None
2800
+ particle_qd = None
2801
+ particle_deltas = None
2802
+
2803
+ body_q = None
2804
+ body_qd = None
2805
+ body_deltas = None
2806
+
2807
+ rigid_contact_inv_weight = None
2808
+
2809
+ if model.rigid_contact_max > 0:
2810
+ if self.rigid_contact_con_weighting:
2811
+ rigid_contact_inv_weight = wp.zeros_like(model.rigid_contact_thickness)
2812
+ rigid_contact_inv_weight_init = None
2813
+
2814
+ if control is None:
2815
+ control = model.control(clone_variables=False)
2816
+
2817
+ with wp.ScopedTimer("simulate", False):
2818
+ if model.particle_count:
2819
+ particle_q = state_out.particle_q
2820
+ particle_qd = state_out.particle_qd
2821
+
2822
+ self.particle_q_init = wp.clone(state_in.particle_q)
2823
+ if self.enable_restitution:
2824
+ self.particle_qd_init = wp.clone(state_in.particle_qd)
2825
+ particle_deltas = wp.empty_like(state_out.particle_qd)
2826
+
2827
+ self.integrate_particles(model, state_in, state_out, dt)
2828
+
2829
+ if model.body_count:
2830
+ body_q = state_out.body_q
2831
+ body_qd = state_out.body_qd
2832
+
2833
+ if self.compute_body_velocity_from_position_delta or self.enable_restitution:
2834
+ body_q_init = wp.clone(state_in.body_q)
2835
+ body_qd_init = wp.clone(state_in.body_qd)
2836
+
2837
+ body_deltas = wp.empty_like(state_out.body_qd)
2838
+
2839
+ if model.joint_count:
2840
+ wp.launch(
2841
+ kernel=apply_joint_actions,
2842
+ dim=model.joint_count,
2843
+ inputs=[
2844
+ state_in.body_q,
2845
+ model.body_com,
2846
+ model.joint_type,
2847
+ model.joint_parent,
2848
+ model.joint_child,
2849
+ model.joint_X_p,
2850
+ model.joint_X_c,
2851
+ model.joint_axis_start,
2852
+ model.joint_axis_dim,
2853
+ model.joint_axis,
2854
+ model.joint_axis_mode,
2855
+ control.joint_act,
2856
+ ],
2857
+ outputs=[state_in.body_f],
2858
+ device=model.device,
2859
+ )
2860
+
2861
+ self.integrate_bodies(model, state_in, state_out, dt, self.angular_damping)
2862
+
2863
+ spring_constraint_lambdas = None
2864
+ if model.spring_count:
2865
+ spring_constraint_lambdas = wp.empty_like(model.spring_rest_length)
2866
+ edge_constraint_lambdas = None
2867
+ if model.edge_count:
2868
+ edge_constraint_lambdas = wp.empty_like(model.edge_rest_angle)
2869
+
2870
+ for i in range(self.iterations):
2871
+ with wp.ScopedTimer(f"iteration_{i}", False):
2872
+ if model.body_count:
2873
+ if requires_grad and i > 0:
2874
+ body_deltas = wp.zeros_like(body_deltas)
2875
+ else:
2876
+ body_deltas.zero_()
2877
+
2878
+ if model.particle_count:
2879
+ if requires_grad and i > 0:
2880
+ particle_deltas = wp.zeros_like(particle_deltas)
2881
+ else:
2882
+ particle_deltas.zero_()
2883
+
2884
+ # particle ground contact
2885
+ if model.ground:
2886
+ wp.launch(
2887
+ kernel=solve_particle_ground_contacts,
2888
+ dim=model.particle_count,
2889
+ inputs=[
2890
+ particle_q,
2891
+ particle_qd,
2892
+ model.particle_inv_mass,
2893
+ model.particle_radius,
2894
+ model.particle_flags,
2895
+ model.soft_contact_ke,
2896
+ model.soft_contact_kd,
2897
+ model.soft_contact_kf,
2898
+ model.soft_contact_mu,
2899
+ model.ground_plane,
2900
+ dt,
2901
+ self.soft_contact_relaxation,
2902
+ ],
2903
+ outputs=[particle_deltas],
2904
+ device=model.device,
2905
+ )
2906
+
2907
+ # particle-rigid body contacts (besides ground plane)
2908
+ if model.shape_count > 1:
2909
+ wp.launch(
2910
+ kernel=solve_particle_shape_contacts,
2911
+ dim=model.soft_contact_max,
2912
+ inputs=[
2913
+ particle_q,
2914
+ particle_qd,
2915
+ model.particle_inv_mass,
2916
+ model.particle_radius,
2917
+ model.particle_flags,
2918
+ body_q,
2919
+ body_qd,
2920
+ model.body_com,
2921
+ model.body_inv_mass,
2922
+ model.body_inv_inertia,
2923
+ model.shape_body,
2924
+ model.shape_materials,
2925
+ model.soft_contact_mu,
2926
+ model.particle_adhesion,
2927
+ model.soft_contact_count,
2928
+ model.soft_contact_particle,
2929
+ model.soft_contact_shape,
2930
+ model.soft_contact_body_pos,
2931
+ model.soft_contact_body_vel,
2932
+ model.soft_contact_normal,
2933
+ model.soft_contact_max,
2934
+ dt,
2935
+ self.soft_contact_relaxation,
2936
+ ],
2937
+ # outputs
2938
+ outputs=[particle_deltas, body_deltas],
2939
+ device=model.device,
2940
+ )
2941
+
2942
+ if model.particle_max_radius > 0.0 and model.particle_count > 1:
2943
+ # assert model.particle_grid.reserved, "model.particle_grid must be built, see HashGrid.build()"
2944
+ wp.launch(
2945
+ kernel=solve_particle_particle_contacts,
2946
+ dim=model.particle_count,
2947
+ inputs=[
2948
+ model.particle_grid.id,
2949
+ particle_q,
2950
+ particle_qd,
2951
+ model.particle_inv_mass,
2952
+ model.particle_radius,
2953
+ model.particle_flags,
2954
+ model.particle_mu,
2955
+ model.particle_cohesion,
2956
+ model.particle_max_radius,
2957
+ dt,
2958
+ self.soft_contact_relaxation,
2959
+ ],
2960
+ outputs=[particle_deltas],
2961
+ device=model.device,
2962
+ )
2963
+
2964
+ # distance constraints
2965
+ if model.spring_count:
2966
+ spring_constraint_lambdas.zero_()
2967
+ wp.launch(
2968
+ kernel=solve_springs,
2969
+ dim=model.spring_count,
2970
+ inputs=[
2971
+ particle_q,
2972
+ particle_qd,
2973
+ model.particle_inv_mass,
2974
+ model.spring_indices,
2975
+ model.spring_rest_length,
2976
+ model.spring_stiffness,
2977
+ model.spring_damping,
2978
+ dt,
2979
+ spring_constraint_lambdas,
2980
+ ],
2981
+ outputs=[particle_deltas],
2982
+ device=model.device,
2983
+ )
2984
+
2985
+ # bending constraints
2986
+ if model.edge_count:
2987
+ edge_constraint_lambdas.zero_()
2988
+ wp.launch(
2989
+ kernel=bending_constraint,
2990
+ dim=model.edge_count,
2991
+ inputs=[
2992
+ particle_q,
2993
+ particle_qd,
2994
+ model.particle_inv_mass,
2995
+ model.edge_indices,
2996
+ model.edge_rest_angle,
2997
+ model.edge_bending_properties,
2998
+ dt,
2999
+ edge_constraint_lambdas,
3000
+ ],
3001
+ outputs=[particle_deltas],
3002
+ device=model.device,
3003
+ )
3004
+
3005
+ # tetrahedral FEM
3006
+ if model.tet_count:
3007
+ wp.launch(
3008
+ kernel=solve_tetrahedra,
3009
+ dim=model.tet_count,
3010
+ inputs=[
3011
+ particle_q,
3012
+ particle_qd,
3013
+ model.particle_inv_mass,
3014
+ model.tet_indices,
3015
+ model.tet_poses,
3016
+ model.tet_activations,
3017
+ model.tet_materials,
3018
+ dt,
3019
+ self.soft_body_relaxation,
3020
+ ],
3021
+ outputs=[particle_deltas],
3022
+ device=model.device,
3023
+ )
3024
+
3025
+ particle_q, particle_qd = self.apply_particle_deltas(
3026
+ model, state_in, state_out, particle_deltas, dt
3027
+ )
3028
+
3029
+ # handle rigid bodies
3030
+ # ----------------------------
3031
+
3032
+ if model.joint_count:
3033
+ # wp.launch(
3034
+ # kernel=solve_simple_body_joints,
3035
+ # dim=model.joint_count,
3036
+ # inputs=[
3037
+ # body_q,
3038
+ # body_qd,
3039
+ # model.body_com,
3040
+ # model.body_inv_mass,
3041
+ # model.body_inv_inertia,
3042
+ # model.joint_type,
3043
+ # model.joint_enabled,
3044
+ # model.joint_parent,
3045
+ # model.joint_child,
3046
+ # model.joint_X_p,
3047
+ # model.joint_X_c,
3048
+ # model.joint_limit_lower,
3049
+ # model.joint_limit_upper,
3050
+ # model.joint_axis_start,
3051
+ # model.joint_axis_dim,
3052
+ # model.joint_axis_mode,
3053
+ # model.joint_axis,
3054
+ # control.joint_target,
3055
+ # model.joint_target_ke,
3056
+ # model.joint_target_kd,
3057
+ # model.joint_linear_compliance,
3058
+ # model.joint_angular_compliance,
3059
+ # self.joint_angular_relaxation,
3060
+ # self.joint_linear_relaxation,
3061
+ # dt,
3062
+ # ],
3063
+ # outputs=[body_deltas],
3064
+ # device=model.device,
3065
+ # )
3066
+
3067
+ wp.launch(
3068
+ kernel=solve_body_joints,
3069
+ dim=model.joint_count,
3070
+ inputs=[
3071
+ body_q,
3072
+ body_qd,
3073
+ model.body_com,
3074
+ model.body_inv_mass,
3075
+ model.body_inv_inertia,
3076
+ model.joint_type,
3077
+ model.joint_enabled,
3078
+ model.joint_parent,
3079
+ model.joint_child,
3080
+ model.joint_X_p,
3081
+ model.joint_X_c,
3082
+ model.joint_limit_lower,
3083
+ model.joint_limit_upper,
3084
+ model.joint_axis_start,
3085
+ model.joint_axis_dim,
3086
+ model.joint_axis_mode,
3087
+ model.joint_axis,
3088
+ control.joint_act,
3089
+ model.joint_target_ke,
3090
+ model.joint_target_kd,
3091
+ model.joint_linear_compliance,
3092
+ model.joint_angular_compliance,
3093
+ self.joint_angular_relaxation,
3094
+ self.joint_linear_relaxation,
3095
+ dt,
3096
+ ],
3097
+ outputs=[body_deltas],
3098
+ device=model.device,
3099
+ )
3100
+
3101
+ body_q, body_qd = self.apply_body_deltas(model, state_in, state_out, body_deltas, dt)
3102
+
3103
+ # Solve rigid contact constraints
3104
+ if model.rigid_contact_max and (
3105
+ model.ground and model.shape_ground_contact_pair_count or model.shape_contact_pair_count
3106
+ ):
3107
+ if self.rigid_contact_con_weighting:
3108
+ rigid_contact_inv_weight.zero_()
3109
+ body_deltas.zero_()
3110
+
3111
+ wp.launch(
3112
+ kernel=solve_body_contact_positions,
3113
+ dim=model.rigid_contact_max,
3114
+ inputs=[
3115
+ body_q,
3116
+ body_qd,
3117
+ model.body_com,
3118
+ model.body_inv_mass,
3119
+ model.body_inv_inertia,
3120
+ model.shape_body,
3121
+ model.rigid_contact_count,
3122
+ model.rigid_contact_point0,
3123
+ model.rigid_contact_point1,
3124
+ model.rigid_contact_offset0,
3125
+ model.rigid_contact_offset1,
3126
+ model.rigid_contact_normal,
3127
+ model.rigid_contact_thickness,
3128
+ model.rigid_contact_shape0,
3129
+ model.rigid_contact_shape1,
3130
+ model.shape_materials,
3131
+ self.rigid_contact_relaxation,
3132
+ dt,
3133
+ model.rigid_contact_torsional_friction,
3134
+ model.rigid_contact_rolling_friction,
3135
+ ],
3136
+ outputs=[
3137
+ body_deltas,
3138
+ rigid_contact_inv_weight,
3139
+ ],
3140
+ device=model.device,
3141
+ )
3142
+
3143
+ # if model.rigid_contact_count.numpy()[0] > 0:
3144
+ # print("rigid_contact_count:", model.rigid_contact_count.numpy().flatten())
3145
+ # # print("rigid_active_contact_distance:", rigid_active_contact_distance.numpy().flatten())
3146
+ # # print("rigid_active_contact_point0:", rigid_active_contact_point0.numpy().flatten())
3147
+ # # print("rigid_active_contact_point1:", rigid_active_contact_point1.numpy().flatten())
3148
+ # print("body_deltas:", body_deltas.numpy().flatten())
3149
+
3150
+ # print(rigid_active_contact_distance.numpy().flatten())
3151
+
3152
+ if self.enable_restitution and i == 0:
3153
+ # remember contact constraint weighting from the first iteration
3154
+ if self.rigid_contact_con_weighting:
3155
+ rigid_contact_inv_weight_init = wp.clone(rigid_contact_inv_weight)
3156
+ else:
3157
+ rigid_contact_inv_weight_init = None
3158
+
3159
+ body_q, body_qd = self.apply_body_deltas(
3160
+ model, state_in, state_out, body_deltas, dt, rigid_contact_inv_weight
3161
+ )
3162
+
3163
+ if model.particle_count:
3164
+ if particle_q.ptr != state_out.particle_q.ptr:
3165
+ state_out.particle_q.assign(particle_q)
3166
+ state_out.particle_qd.assign(particle_qd)
3167
+
3168
+ if model.body_count:
3169
+ if body_q.ptr != state_out.body_q.ptr:
3170
+ state_out.body_q.assign(body_q)
3171
+ state_out.body_qd.assign(body_qd)
3172
+
3173
+ # update body velocities from position changes
3174
+ if self.compute_body_velocity_from_position_delta and model.body_count and not requires_grad:
3175
+ # causes gradient issues (probably due to numerical problems
3176
+ # when computing velocities from position changes)
3177
+ if requires_grad:
3178
+ out_body_qd = wp.clone(state_out.body_qd)
3179
+ else:
3180
+ out_body_qd = state_out.body_qd
3181
+
3182
+ # update body velocities
3183
+ wp.launch(
3184
+ kernel=update_body_velocities,
3185
+ dim=model.body_count,
3186
+ inputs=[state_out.body_q, body_q_init, model.body_com, dt],
3187
+ outputs=[out_body_qd],
3188
+ device=model.device,
3189
+ )
3190
+
3191
+ if self.enable_restitution:
3192
+ if model.particle_count:
3193
+ wp.launch(
3194
+ kernel=apply_particle_shape_restitution,
3195
+ dim=model.particle_count,
3196
+ inputs=[
3197
+ particle_q,
3198
+ particle_qd,
3199
+ self.particle_q_init,
3200
+ self.particle_qd_init,
3201
+ model.particle_inv_mass,
3202
+ model.particle_radius,
3203
+ model.particle_flags,
3204
+ body_q,
3205
+ body_qd,
3206
+ model.body_com,
3207
+ model.body_inv_mass,
3208
+ model.body_inv_inertia,
3209
+ model.shape_body,
3210
+ model.shape_materials,
3211
+ model.particle_adhesion,
3212
+ model.soft_contact_restitution,
3213
+ model.soft_contact_count,
3214
+ model.soft_contact_particle,
3215
+ model.soft_contact_shape,
3216
+ model.soft_contact_body_pos,
3217
+ model.soft_contact_body_vel,
3218
+ model.soft_contact_normal,
3219
+ model.soft_contact_max,
3220
+ dt,
3221
+ self.soft_contact_relaxation,
3222
+ ],
3223
+ outputs=[state_out.particle_qd],
3224
+ device=model.device,
3225
+ )
3226
+ if model.ground:
3227
+ wp.launch(
3228
+ kernel=apply_particle_ground_restitution,
3229
+ dim=model.particle_count,
3230
+ inputs=[
3231
+ particle_q,
3232
+ particle_qd,
3233
+ self.particle_q_init,
3234
+ self.particle_qd_init,
3235
+ model.particle_inv_mass,
3236
+ model.particle_radius,
3237
+ model.particle_flags,
3238
+ model.particle_adhesion,
3239
+ model.soft_contact_restitution,
3240
+ model.ground_plane,
3241
+ dt,
3242
+ self.soft_contact_relaxation,
3243
+ ],
3244
+ outputs=[state_out.particle_qd],
3245
+ device=model.device,
3246
+ )
3247
+
3248
+ if model.body_count:
3249
+ body_deltas.zero_()
3250
+ wp.launch(
3251
+ kernel=apply_rigid_restitution,
3252
+ dim=model.rigid_contact_max,
3253
+ inputs=[
3254
+ state_out.body_q,
3255
+ state_out.body_qd,
3256
+ body_q_init,
3257
+ body_qd_init,
3258
+ model.body_com,
3259
+ model.body_inv_mass,
3260
+ model.body_inv_inertia,
3261
+ model.shape_body,
3262
+ model.rigid_contact_count,
3263
+ model.rigid_contact_normal,
3264
+ model.rigid_contact_shape0,
3265
+ model.rigid_contact_shape1,
3266
+ model.shape_materials,
3267
+ model.rigid_contact_point0,
3268
+ model.rigid_contact_point1,
3269
+ model.rigid_contact_offset0,
3270
+ model.rigid_contact_offset1,
3271
+ model.rigid_contact_thickness,
3272
+ rigid_contact_inv_weight_init,
3273
+ model.gravity,
3274
+ dt,
3275
+ ],
3276
+ outputs=[
3277
+ body_deltas,
3278
+ ],
3279
+ device=model.device,
3280
+ )
3281
+
3282
+ wp.launch(
3283
+ kernel=apply_body_delta_velocities,
3284
+ dim=model.body_count,
3285
+ inputs=[
3286
+ body_deltas,
3287
+ ],
3288
+ outputs=[state_out.body_qd],
3289
+ device=model.device,
3290
+ )
3291
+
3292
+ return state_out