warp-lang 0.10.1__py3-none-win_amd64.whl → 0.11.0__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of warp-lang might be problematic. Click here for more details.

Files changed (300) hide show
  1. warp/__init__.py +10 -4
  2. warp/__init__.pyi +1 -0
  3. warp/bin/warp-clang.dll +0 -0
  4. warp/bin/warp.dll +0 -0
  5. warp/build.py +5 -3
  6. warp/build_dll.py +29 -9
  7. warp/builtins.py +868 -507
  8. warp/codegen.py +1074 -638
  9. warp/config.py +3 -3
  10. warp/constants.py +6 -0
  11. warp/context.py +715 -222
  12. warp/fabric.py +326 -0
  13. warp/fem/__init__.py +27 -0
  14. warp/fem/cache.py +389 -0
  15. warp/fem/dirichlet.py +181 -0
  16. warp/fem/domain.py +263 -0
  17. warp/fem/field/__init__.py +101 -0
  18. warp/fem/field/field.py +149 -0
  19. warp/fem/field/nodal_field.py +299 -0
  20. warp/fem/field/restriction.py +21 -0
  21. warp/fem/field/test.py +181 -0
  22. warp/fem/field/trial.py +183 -0
  23. warp/fem/geometry/__init__.py +19 -0
  24. warp/fem/geometry/closest_point.py +70 -0
  25. warp/fem/geometry/deformed_geometry.py +271 -0
  26. warp/fem/geometry/element.py +744 -0
  27. warp/fem/geometry/geometry.py +186 -0
  28. warp/fem/geometry/grid_2d.py +373 -0
  29. warp/fem/geometry/grid_3d.py +435 -0
  30. warp/fem/geometry/hexmesh.py +953 -0
  31. warp/fem/geometry/partition.py +376 -0
  32. warp/fem/geometry/quadmesh_2d.py +532 -0
  33. warp/fem/geometry/tetmesh.py +840 -0
  34. warp/fem/geometry/trimesh_2d.py +577 -0
  35. warp/fem/integrate.py +1616 -0
  36. warp/fem/operator.py +191 -0
  37. warp/fem/polynomial.py +213 -0
  38. warp/fem/quadrature/__init__.py +2 -0
  39. warp/fem/quadrature/pic_quadrature.py +245 -0
  40. warp/fem/quadrature/quadrature.py +294 -0
  41. warp/fem/space/__init__.py +292 -0
  42. warp/fem/space/basis_space.py +489 -0
  43. warp/fem/space/collocated_function_space.py +105 -0
  44. warp/fem/space/dof_mapper.py +236 -0
  45. warp/fem/space/function_space.py +145 -0
  46. warp/fem/space/grid_2d_function_space.py +267 -0
  47. warp/fem/space/grid_3d_function_space.py +306 -0
  48. warp/fem/space/hexmesh_function_space.py +352 -0
  49. warp/fem/space/partition.py +350 -0
  50. warp/fem/space/quadmesh_2d_function_space.py +369 -0
  51. warp/fem/space/restriction.py +160 -0
  52. warp/fem/space/shape/__init__.py +15 -0
  53. warp/fem/space/shape/cube_shape_function.py +738 -0
  54. warp/fem/space/shape/shape_function.py +103 -0
  55. warp/fem/space/shape/square_shape_function.py +611 -0
  56. warp/fem/space/shape/tet_shape_function.py +567 -0
  57. warp/fem/space/shape/triangle_shape_function.py +429 -0
  58. warp/fem/space/tetmesh_function_space.py +292 -0
  59. warp/fem/space/topology.py +295 -0
  60. warp/fem/space/trimesh_2d_function_space.py +221 -0
  61. warp/fem/types.py +77 -0
  62. warp/fem/utils.py +495 -0
  63. warp/native/array.h +147 -44
  64. warp/native/builtin.h +122 -149
  65. warp/native/bvh.cpp +73 -325
  66. warp/native/bvh.cu +406 -23
  67. warp/native/bvh.h +34 -43
  68. warp/native/clang/clang.cpp +13 -8
  69. warp/native/crt.h +2 -0
  70. warp/native/cuda_crt.h +5 -0
  71. warp/native/cuda_util.cpp +15 -3
  72. warp/native/cuda_util.h +3 -1
  73. warp/native/cutlass/tools/library/scripts/conv2d_operation.py +463 -0
  74. warp/native/cutlass/tools/library/scripts/conv3d_operation.py +321 -0
  75. warp/native/cutlass/tools/library/scripts/gemm_operation.py +988 -0
  76. warp/native/cutlass/tools/library/scripts/generator.py +4625 -0
  77. warp/native/cutlass/tools/library/scripts/library.py +799 -0
  78. warp/native/cutlass/tools/library/scripts/manifest.py +402 -0
  79. warp/native/cutlass/tools/library/scripts/pycutlass/docs/source/conf.py +96 -0
  80. warp/native/cutlass/tools/library/scripts/pycutlass/profile/conv/conv2d_f16_sm80.py +106 -0
  81. warp/native/cutlass/tools/library/scripts/pycutlass/profile/gemm/gemm_f32_sm80.py +91 -0
  82. warp/native/cutlass/tools/library/scripts/pycutlass/setup.py +80 -0
  83. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/__init__.py +48 -0
  84. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/arguments.py +118 -0
  85. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/c_types.py +241 -0
  86. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/compiler.py +432 -0
  87. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/conv2d_operation.py +631 -0
  88. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/epilogue.py +1026 -0
  89. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/frontend.py +104 -0
  90. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/gemm_operation.py +1276 -0
  91. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/library.py +744 -0
  92. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/memory_manager.py +74 -0
  93. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/operation.py +110 -0
  94. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/parser.py +619 -0
  95. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/reduction_operation.py +398 -0
  96. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/tensor_ref.py +70 -0
  97. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/__init__.py +4 -0
  98. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/conv2d_testbed.py +646 -0
  99. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/gemm_grouped_testbed.py +235 -0
  100. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/gemm_testbed.py +557 -0
  101. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/test/profiler.py +70 -0
  102. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/type_hint.py +39 -0
  103. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/utils/__init__.py +1 -0
  104. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/utils/device.py +76 -0
  105. warp/native/cutlass/tools/library/scripts/pycutlass/src/pycutlass/utils/reference_model.py +255 -0
  106. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/__init__.py +0 -0
  107. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.py +201 -0
  108. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +177 -0
  109. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_f32nhwc_f32nhwc_f32nhwc_simt_f32_sm80.py +98 -0
  110. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_dgrad_implicit_gemm_tf32nhwc_tf32nhwc_f32nhwc_tensor_op_f32_sm80.py +95 -0
  111. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_few_channels_f16nhwc_f16nhwc_f16nhwc_tensor_op_f32_sm80.py +163 -0
  112. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_fixed_channels_f16nhwc_f16nhwc_f16nhwc_tensor_op_f32_sm80.py +187 -0
  113. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.py +309 -0
  114. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +54 -0
  115. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_f32nhwc_f32nhwc_f32nhwc_simt_f32_sm80.py +96 -0
  116. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_fprop_implicit_gemm_tf32nhwc_tf32nhwc_f32nhwc_tensor_op_f32_sm80.py +107 -0
  117. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_strided_dgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +253 -0
  118. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f16nhwc_tensor_op_f16_sm80.py +97 -0
  119. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_f16nhwc_f16nhwc_f32nhwc_tensor_op_f32_sm80.py +242 -0
  120. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_f32nhwc_f32nhwc_f32nhwc_simt_f32_sm80.py +96 -0
  121. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/conv2d_wgrad_implicit_gemm_tf32nhwc_tf32nhwc_f32nhwc_tensor_op_f32_sm80.py +107 -0
  122. warp/native/cutlass/tools/library/scripts/pycutlass/test/conv/run_all_tests.py +10 -0
  123. warp/native/cutlass/tools/library/scripts/pycutlass/test/frontend/test_frontend.py +146 -0
  124. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/__init__.py +0 -0
  125. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_bf16_sm80.py +96 -0
  126. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_f16_sm80.py +447 -0
  127. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_f32_sm80.py +146 -0
  128. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_f64_sm80.py +102 -0
  129. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_grouped_sm80.py +203 -0
  130. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/gemm_s8_sm80.py +229 -0
  131. warp/native/cutlass/tools/library/scripts/pycutlass/test/gemm/run_all_tests.py +9 -0
  132. warp/native/cutlass/tools/library/scripts/pycutlass/test/unit/test_sm80.py +453 -0
  133. warp/native/cutlass/tools/library/scripts/rank_2k_operation.py +398 -0
  134. warp/native/cutlass/tools/library/scripts/rank_k_operation.py +387 -0
  135. warp/native/cutlass/tools/library/scripts/rt.py +796 -0
  136. warp/native/cutlass/tools/library/scripts/symm_operation.py +400 -0
  137. warp/native/cutlass/tools/library/scripts/trmm_operation.py +407 -0
  138. warp/native/cutlass_gemm.cu +5 -3
  139. warp/native/exports.h +1240 -952
  140. warp/native/fabric.h +228 -0
  141. warp/native/hashgrid.cpp +4 -4
  142. warp/native/hashgrid.h +22 -2
  143. warp/native/intersect.h +22 -7
  144. warp/native/intersect_adj.h +8 -8
  145. warp/native/intersect_tri.h +1 -1
  146. warp/native/marching.cu +157 -161
  147. warp/native/mat.h +80 -19
  148. warp/native/matnn.h +2 -2
  149. warp/native/mesh.cpp +33 -108
  150. warp/native/mesh.cu +114 -23
  151. warp/native/mesh.h +446 -46
  152. warp/native/noise.h +272 -329
  153. warp/native/quat.h +51 -8
  154. warp/native/rand.h +45 -35
  155. warp/native/range.h +6 -2
  156. warp/native/reduce.cpp +1 -1
  157. warp/native/reduce.cu +10 -12
  158. warp/native/runlength_encode.cu +6 -10
  159. warp/native/scan.cu +8 -11
  160. warp/native/sparse.cpp +4 -4
  161. warp/native/sparse.cu +164 -154
  162. warp/native/spatial.h +2 -2
  163. warp/native/temp_buffer.h +14 -30
  164. warp/native/vec.h +107 -23
  165. warp/native/volume.h +120 -0
  166. warp/native/warp.cpp +560 -30
  167. warp/native/warp.cu +431 -44
  168. warp/native/warp.h +13 -4
  169. warp/optim/__init__.py +1 -0
  170. warp/optim/linear.py +922 -0
  171. warp/optim/sgd.py +92 -0
  172. warp/render/render_opengl.py +335 -119
  173. warp/render/render_usd.py +11 -11
  174. warp/sim/__init__.py +2 -2
  175. warp/sim/articulation.py +385 -185
  176. warp/sim/collide.py +8 -0
  177. warp/sim/import_mjcf.py +297 -106
  178. warp/sim/import_urdf.py +389 -210
  179. warp/sim/import_usd.py +198 -97
  180. warp/sim/inertia.py +17 -18
  181. warp/sim/integrator_euler.py +14 -8
  182. warp/sim/integrator_xpbd.py +158 -16
  183. warp/sim/model.py +795 -291
  184. warp/sim/render.py +3 -3
  185. warp/sim/utils.py +3 -0
  186. warp/sparse.py +640 -150
  187. warp/stubs.py +606 -267
  188. warp/tape.py +61 -10
  189. warp/tests/__main__.py +3 -6
  190. warp/tests/assets/curlnoise_golden.npy +0 -0
  191. warp/tests/assets/pnoise_golden.npy +0 -0
  192. warp/tests/{test_class_kernel.py → aux_test_class_kernel.py} +9 -1
  193. warp/tests/aux_test_conditional_unequal_types_kernels.py +21 -0
  194. warp/tests/{test_dependent.py → aux_test_dependent.py} +2 -2
  195. warp/tests/{test_reference.py → aux_test_reference.py} +1 -1
  196. warp/tests/aux_test_unresolved_func.py +14 -0
  197. warp/tests/aux_test_unresolved_symbol.py +14 -0
  198. warp/tests/disabled_kinematics.py +239 -0
  199. warp/tests/run_coverage_serial.py +31 -0
  200. warp/tests/test_adam.py +103 -106
  201. warp/tests/test_arithmetic.py +128 -74
  202. warp/tests/test_array.py +212 -97
  203. warp/tests/test_array_reduce.py +57 -23
  204. warp/tests/test_atomic.py +64 -28
  205. warp/tests/test_bool.py +99 -0
  206. warp/tests/test_builtins_resolution.py +1292 -0
  207. warp/tests/test_bvh.py +42 -18
  208. warp/tests/test_closest_point_edge_edge.py +54 -57
  209. warp/tests/test_codegen.py +208 -130
  210. warp/tests/test_compile_consts.py +28 -20
  211. warp/tests/test_conditional.py +108 -24
  212. warp/tests/test_copy.py +10 -12
  213. warp/tests/test_ctypes.py +112 -88
  214. warp/tests/test_dense.py +21 -14
  215. warp/tests/test_devices.py +98 -0
  216. warp/tests/test_dlpack.py +75 -75
  217. warp/tests/test_examples.py +277 -0
  218. warp/tests/test_fabricarray.py +955 -0
  219. warp/tests/test_fast_math.py +15 -11
  220. warp/tests/test_fem.py +1271 -0
  221. warp/tests/test_fp16.py +53 -19
  222. warp/tests/test_func.py +187 -86
  223. warp/tests/test_generics.py +194 -49
  224. warp/tests/test_grad.py +178 -109
  225. warp/tests/test_grad_customs.py +176 -0
  226. warp/tests/test_hash_grid.py +52 -37
  227. warp/tests/test_import.py +10 -23
  228. warp/tests/test_indexedarray.py +32 -31
  229. warp/tests/test_intersect.py +18 -9
  230. warp/tests/test_large.py +141 -0
  231. warp/tests/test_launch.py +14 -41
  232. warp/tests/test_lerp.py +64 -65
  233. warp/tests/test_linear_solvers.py +154 -0
  234. warp/tests/test_lvalue.py +493 -0
  235. warp/tests/test_marching_cubes.py +12 -13
  236. warp/tests/test_mat.py +517 -2898
  237. warp/tests/test_mat_lite.py +115 -0
  238. warp/tests/test_mat_scalar_ops.py +2889 -0
  239. warp/tests/test_math.py +103 -9
  240. warp/tests/test_matmul.py +305 -69
  241. warp/tests/test_matmul_lite.py +410 -0
  242. warp/tests/test_mesh.py +71 -14
  243. warp/tests/test_mesh_query_aabb.py +41 -25
  244. warp/tests/test_mesh_query_point.py +140 -22
  245. warp/tests/test_mesh_query_ray.py +39 -22
  246. warp/tests/test_mlp.py +30 -22
  247. warp/tests/test_model.py +92 -89
  248. warp/tests/test_modules_lite.py +39 -0
  249. warp/tests/test_multigpu.py +88 -114
  250. warp/tests/test_noise.py +12 -11
  251. warp/tests/test_operators.py +16 -20
  252. warp/tests/test_options.py +11 -11
  253. warp/tests/test_pinned.py +17 -18
  254. warp/tests/test_print.py +32 -11
  255. warp/tests/test_quat.py +275 -129
  256. warp/tests/test_rand.py +18 -16
  257. warp/tests/test_reload.py +38 -34
  258. warp/tests/test_rounding.py +50 -43
  259. warp/tests/test_runlength_encode.py +168 -20
  260. warp/tests/test_smoothstep.py +9 -11
  261. warp/tests/test_snippet.py +143 -0
  262. warp/tests/test_sparse.py +261 -63
  263. warp/tests/test_spatial.py +276 -243
  264. warp/tests/test_streams.py +110 -85
  265. warp/tests/test_struct.py +268 -63
  266. warp/tests/test_tape.py +39 -21
  267. warp/tests/test_torch.py +118 -89
  268. warp/tests/test_transient_module.py +12 -13
  269. warp/tests/test_types.py +614 -0
  270. warp/tests/test_utils.py +494 -0
  271. warp/tests/test_vec.py +354 -2050
  272. warp/tests/test_vec_lite.py +73 -0
  273. warp/tests/test_vec_scalar_ops.py +2099 -0
  274. warp/tests/test_volume.py +457 -293
  275. warp/tests/test_volume_write.py +124 -134
  276. warp/tests/unittest_serial.py +35 -0
  277. warp/tests/unittest_suites.py +341 -0
  278. warp/tests/unittest_utils.py +568 -0
  279. warp/tests/unused_test_misc.py +71 -0
  280. warp/tests/{test_debug.py → walkthough_debug.py} +3 -17
  281. warp/thirdparty/appdirs.py +36 -45
  282. warp/thirdparty/unittest_parallel.py +549 -0
  283. warp/torch.py +9 -6
  284. warp/types.py +1089 -366
  285. warp/utils.py +93 -387
  286. warp_lang-0.11.0.dist-info/METADATA +238 -0
  287. warp_lang-0.11.0.dist-info/RECORD +332 -0
  288. {warp_lang-0.10.1.dist-info → warp_lang-0.11.0.dist-info}/WHEEL +1 -1
  289. warp/tests/test_all.py +0 -219
  290. warp/tests/test_array_scan.py +0 -60
  291. warp/tests/test_base.py +0 -208
  292. warp/tests/test_unresolved_func.py +0 -7
  293. warp/tests/test_unresolved_symbol.py +0 -7
  294. warp_lang-0.10.1.dist-info/METADATA +0 -21
  295. warp_lang-0.10.1.dist-info/RECORD +0 -188
  296. /warp/tests/{test_compile_consts_dummy.py → aux_test_compile_consts_dummy.py} +0 -0
  297. /warp/tests/{test_reference_reference.py → aux_test_reference_reference.py} +0 -0
  298. /warp/tests/{test_square.py → aux_test_square.py} +0 -0
  299. {warp_lang-0.10.1.dist-info → warp_lang-0.11.0.dist-info}/LICENSE.md +0 -0
  300. {warp_lang-0.10.1.dist-info → warp_lang-0.11.0.dist-info}/top_level.txt +0 -0
warp/sim/model.py CHANGED
@@ -8,21 +8,23 @@
8
8
  """A module for building simulation models and state.
9
9
  """
10
10
 
11
- from .inertia import transform_inertia
12
- from .inertia import compute_cone_inertia
13
- from .inertia import compute_cylinder_inertia
14
- from .inertia import compute_capsule_inertia
15
- from .inertia import compute_box_inertia
16
- from .inertia import compute_sphere_inertia
17
- from .inertia import compute_mesh_inertia
11
+ import copy
12
+ import math
13
+ from typing import List, Optional, Tuple
18
14
 
19
- import warp as wp
20
15
  import numpy as np
21
16
 
22
- import math
23
- import copy
17
+ import warp as wp
24
18
 
25
- from typing import List, Optional, Tuple
19
+ from .inertia import (
20
+ compute_box_inertia,
21
+ compute_capsule_inertia,
22
+ compute_cone_inertia,
23
+ compute_cylinder_inertia,
24
+ compute_mesh_inertia,
25
+ compute_sphere_inertia,
26
+ transform_inertia,
27
+ )
26
28
 
27
29
  Vec3 = List[float]
28
30
  Vec4 = List[float]
@@ -92,11 +94,27 @@ class ModelShapeGeometry:
92
94
 
93
95
  # Axis (linear or angular) of a joint that can have bounds and be driven towards a target
94
96
  class JointAxis:
97
+ """
98
+ Describes a joint axis that can have limits and be driven towards a target.
99
+
100
+ Attributes:
101
+
102
+ axis (3D vector or JointAxis): The 3D axis that this JointAxis object describes, or alternatively another JointAxis object to copy from
103
+ limit_lower (float): The lower limit of the joint axis
104
+ limit_upper (float): The upper limit of the joint axis
105
+ limit_ke (float): The elastic stiffness of the joint axis limits, only respected by :class:`SemiImplicitIntegrator`
106
+ limit_kd (float): The damping stiffness of the joint axis limits, only respected by :class:`SemiImplicitIntegrator`
107
+ target (float): The target position or velocity (depending on the mode, see `Joint modes`_) of the joint axis
108
+ target_ke (float): The proportional gain of the joint axis target drive PD controller
109
+ target_kd (float): The derivative gain of the joint axis target drive PD controller
110
+ mode (int): The mode of the joint axis, see `Joint modes`_
111
+ """
112
+
95
113
  def __init__(
96
114
  self,
97
115
  axis,
98
- limit_lower=-np.inf,
99
- limit_upper=np.inf,
116
+ limit_lower=-math.inf,
117
+ limit_upper=math.inf,
100
118
  limit_ke=100.0,
101
119
  limit_kd=10.0,
102
120
  target=None,
@@ -104,30 +122,92 @@ class JointAxis:
104
122
  target_kd=0.0,
105
123
  mode=JOINT_MODE_TARGET_POSITION,
106
124
  ):
107
- self.axis = np.array(wp.normalize(np.array(axis, dtype=np.float32)))
108
- self.limit_lower = limit_lower
109
- self.limit_upper = limit_upper
110
- self.limit_ke = limit_ke
111
- self.limit_kd = limit_kd
112
- if target is not None:
113
- self.target = target
114
- elif limit_lower > 0.0 or limit_upper < 0.0:
115
- self.target = 0.5 * (limit_lower + limit_upper)
125
+ if isinstance(axis, JointAxis):
126
+ self.axis = axis.axis
127
+ self.limit_lower = axis.limit_lower
128
+ self.limit_upper = axis.limit_upper
129
+ self.limit_ke = axis.limit_ke
130
+ self.limit_kd = axis.limit_kd
131
+ self.target = axis.target
132
+ self.target_ke = axis.target_ke
133
+ self.target_kd = axis.target_kd
134
+ self.mode = axis.mode
116
135
  else:
117
- self.target = 0.0
118
- self.target_ke = target_ke
119
- self.target_kd = target_kd
120
- self.mode = mode
136
+ self.axis = wp.normalize(wp.vec3(axis))
137
+ self.limit_lower = limit_lower
138
+ self.limit_upper = limit_upper
139
+ self.limit_ke = limit_ke
140
+ self.limit_kd = limit_kd
141
+ if target is not None:
142
+ self.target = target
143
+ elif limit_lower > 0.0 or limit_upper < 0.0:
144
+ self.target = 0.5 * (limit_lower + limit_upper)
145
+ else:
146
+ self.target = 0.0
147
+ self.target_ke = target_ke
148
+ self.target_kd = target_kd
149
+ self.mode = mode
150
+
151
+
152
+ class SDF:
153
+ """Describes a signed distance field for simulation
154
+
155
+ Attributes:
156
+
157
+ volume (Volume): The volume defining the SDF
158
+ I (Mat33): 3x3 inertia matrix of the SDF
159
+ mass (float): The total mass of the SDF
160
+ com (Vec3): The center of mass of the SDF
161
+ """
162
+ def __init__(self, volume=None, I=wp.mat33(np.eye(3)), mass=1.0, com=wp.vec3()):
163
+ self.volume = volume
164
+ self.I = I
165
+ self.mass = mass
166
+ self.com = com
167
+
168
+ # Need to specify these for now
169
+ self.has_inertia = True
170
+ self.is_solid = True
171
+
172
+ def finalize(self, device=None):
173
+ return self.volume.id
174
+
175
+ def __hash__(self):
176
+ return hash((self.volume.id))
121
177
 
122
178
 
123
179
  class Mesh:
124
180
  """Describes a triangle collision mesh for simulation
181
+
182
+ Example mesh creation from a triangle OBJ mesh file:
183
+ ====================================================
184
+
185
+ .. code-block:: python
186
+
187
+ import numpy as np
188
+ import warp as wp
189
+ import warp.sim
190
+
191
+ def load_mesh(self, filename, use_meshio=True):
192
+ if use_meshio:
193
+ import meshio
194
+ m = meshio.read(filename)
195
+ mesh_points = np.array(m.points)
196
+ mesh_indices = np.array(m.cells[0].data, dtype=np.int32).flatten()
197
+ else:
198
+ import openmesh
199
+ m = openmesh.read_trimesh(filename)
200
+ mesh_points = np.array(m.points())
201
+ mesh_indices = np.array(m.face_vertex_indices(), dtype=np.int32).flatten()
202
+ return wp.sim.Mesh(mesh_points, mesh_indices)
203
+
204
+ mesh = load_mesh("mesh.obj")
125
205
 
126
206
  Attributes:
127
207
 
128
- vertices (List[Vec3]): Mesh vertices
129
- indices (List[int]): Mesh indices
130
- I (Mat33): Inertia tensor of the mesh assuming density of 1.0 (around the center of mass)
208
+ vertices (List[Vec3]): Mesh 3D vertices points
209
+ indices (List[int]): Mesh indices as a flattened list of vertex indices describing triangles
210
+ I (Mat33): 3x3 inertia matrix of the mesh assuming density of 1.0 (around the center of mass)
131
211
  mass (float): The total mass of the body assuming density of 1.0
132
212
  com (Vec3): The center of mass of the body
133
213
  """
@@ -146,20 +226,28 @@ class Mesh:
146
226
  is_solid: If True, the mesh is assumed to be a solid during inertia computation, otherwise it is assumed to be a hollow surface
147
227
  """
148
228
 
149
- self.vertices = vertices
150
- self.indices = indices
229
+ self.vertices = np.array(vertices).reshape(-1, 3)
230
+ self.indices = np.array(indices, dtype=np.int32).flatten()
151
231
  self.is_solid = is_solid
152
232
  self.has_inertia = compute_inertia
153
233
 
154
234
  if compute_inertia:
155
235
  self.mass, self.com, self.I, _ = compute_mesh_inertia(1.0, vertices, indices, is_solid=is_solid)
156
236
  else:
157
- self.I = np.eye(3, dtype=np.float32)
237
+ self.I = wp.mat33(np.eye(3))
158
238
  self.mass = 1.0
159
- self.com = np.array((0.0, 0.0, 0.0))
239
+ self.com = wp.vec3()
160
240
 
161
- # construct simulation ready buffers from points
162
241
  def finalize(self, device=None):
242
+ """
243
+ Constructs a simulation-ready :class:`Mesh` object from the mesh data and returns its ID.
244
+
245
+ Args:
246
+ device: The device on which to allocate the mesh buffers
247
+
248
+ Returns:
249
+ The ID of the simulation-ready :class:`Mesh`
250
+ """
163
251
  with wp.ScopedDevice(device):
164
252
  pos = wp.array(self.vertices, dtype=wp.vec3)
165
253
  vel = wp.zeros_like(pos)
@@ -169,6 +257,9 @@ class Mesh:
169
257
  return self.mesh.id
170
258
 
171
259
  def __hash__(self):
260
+ """
261
+ Computes a hash of the mesh data for use in caching. The hash considers the mesh vertices, indices, and whether the mesh is solid or not.
262
+ """
172
263
  return hash((tuple(np.array(self.vertices).flatten()), tuple(np.array(self.indices).flatten()), self.is_solid))
173
264
 
174
265
 
@@ -183,11 +274,16 @@ class State:
183
274
 
184
275
  Attributes:
185
276
 
186
- particle_q (wp.array): Tensor of particle positions
187
- particle_qd (wp.array): Tensor of particle velocities
277
+ particle_count (int): Number of particles
278
+ body_count (int): Number of rigid bodies
279
+
280
+ particle_q (array): Tensor of particle positions
281
+ particle_qd (array): Tensor of particle velocities
282
+ particle_f (array): Tensor of particle forces
188
283
 
189
- body_q (wp.array): Tensor of body coordinates
190
- body_qd (wp.array): Tensor of body velocities
284
+ body_q (array): Tensor of body coordinates
285
+ body_qd (array): Tensor of body velocities
286
+ body_f (array): Tensor of body forces
191
287
 
192
288
  """
193
289
 
@@ -195,6 +291,14 @@ class State:
195
291
  self.particle_count = 0
196
292
  self.body_count = 0
197
293
 
294
+ self.particle_q = None
295
+ self.particle_qd = None
296
+ self.particle_f = None
297
+
298
+ self.body_q = None
299
+ self.body_qd = None
300
+ self.body_f = None
301
+
198
302
  def clear_forces(self):
199
303
  if self.particle_count:
200
304
  self.particle_f.zero_()
@@ -203,25 +307,25 @@ class State:
203
307
  self.body_f.zero_()
204
308
 
205
309
  def flatten(self):
206
- """Returns a list of Tensors stored by the state
207
-
208
- This function is intended to be used internal-only but can be used to obtain
209
- a set of all tensors owned by the state.
210
- """
310
+ wp.utils.warn(
311
+ "Model.flatten() will be removed in a future Warp version.",
312
+ DeprecationWarning,
313
+ stacklevel=2,
314
+ )
211
315
 
212
- tensors = []
316
+ arrays = []
213
317
 
214
318
  # build a list of all tensor attributes
215
319
  for attr, value in self.__dict__.items():
216
- if wp.is_tensor(value):
217
- tensors.append(value)
320
+ if isinstance(value, wp.array):
321
+ arrays.append(value)
218
322
 
219
- return tensors
323
+ return arrays
220
324
 
221
325
 
222
326
  def compute_shape_mass(type, scale, src, density, is_solid, thickness):
223
327
  if density == 0.0 or type == GEO_PLANE: # zero density means fixed
224
- return 0.0, np.zeros(3), np.zeros((3, 3))
328
+ return 0.0, wp.vec3(), wp.mat33()
225
329
 
226
330
  if type == GEO_SPHERE:
227
331
  solid = compute_sphere_inertia(density, scale[0])
@@ -231,7 +335,7 @@ def compute_shape_mass(type, scale, src, density, is_solid, thickness):
231
335
  hollow = compute_sphere_inertia(density, scale[0] - thickness)
232
336
  return solid[0] - hollow[0], solid[1], solid[2] - hollow[2]
233
337
  elif type == GEO_BOX:
234
- w, h, d = np.array(scale[:3]) * 2.0
338
+ w, h, d = scale * 2.0
235
339
  solid = compute_box_inertia(density, w, h, d)
236
340
  if is_solid:
237
341
  return solid
@@ -262,17 +366,16 @@ def compute_shape_mass(type, scale, src, density, is_solid, thickness):
262
366
  else:
263
367
  hollow = compute_cone_inertia(density, r - thickness, h - 2.0 * thickness)
264
368
  return solid[0] - hollow[0], solid[1], solid[2] - hollow[2]
265
- elif type == GEO_MESH:
369
+ elif type == GEO_MESH or type == GEO_SDF:
266
370
  if src.has_inertia and src.mass > 0.0 and src.is_solid == is_solid:
267
371
  m, c, I = src.mass, src.com, src.I
268
372
 
269
- s = np.array(scale[:3])
270
- sx, sy, sz = s
373
+ sx, sy, sz = scale
271
374
 
272
375
  mass_ratio = sx * sy * sz * density
273
376
  m_new = m * mass_ratio
274
377
 
275
- c_new = c * s
378
+ c_new = wp.cw_mul(c, scale)
276
379
 
277
380
  Ixx = I[0, 0] * (sy**2 + sz**2) / 2 * mass_ratio
278
381
  Iyy = I[1, 1] * (sx**2 + sz**2) / 2 * mass_ratio
@@ -281,12 +384,12 @@ def compute_shape_mass(type, scale, src, density, is_solid, thickness):
281
384
  Ixz = I[0, 2] * sx * sz * mass_ratio
282
385
  Iyz = I[1, 2] * sy * sz * mass_ratio
283
386
 
284
- I_new = np.array([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]])
387
+ I_new = wp.mat33([[Ixx, Ixy, Ixz], [Ixy, Iyy, Iyz], [Ixz, Iyz, Izz]])
285
388
 
286
389
  return m_new, c_new, I_new
287
- else:
390
+ elif type == GEO_MESH:
288
391
  # fall back to computing inertia from mesh geometry
289
- vertices = np.array(src.vertices) * np.array(scale[:3])
392
+ vertices = np.array(src.vertices) * np.array(scale)
290
393
  m, c, I, vol = compute_mesh_inertia(density, vertices, src.indices, is_solid, thickness)
291
394
  return m, c, I
292
395
  raise ValueError("Unsupported shape type: {}".format(type))
@@ -302,23 +405,24 @@ class Model:
302
405
  requires_grad (float): Indicates whether the model was finalized with gradient computation enabled
303
406
  num_envs (int): Number of articulation environments that were added to the ModelBuilder via `add_builder`
304
407
 
305
- particle_q (wp.array): Particle positions, shape [particle_count, 3], float
306
- particle_qd (wp.array): Particle velocities, shape [particle_count, 3], float
307
- particle_mass (wp.array): Particle mass, shape [particle_count], float
308
- particle_inv_mass (wp.array): Particle inverse mass, shape [particle_count], float
309
- particle_radius (wp.array): Particle radius, shape [particle_count], float
408
+ particle_q (array): Particle positions, shape [particle_count, 3], float
409
+ particle_qd (array): Particle velocities, shape [particle_count, 3], float
410
+ particle_mass (array): Particle mass, shape [particle_count], float
411
+ particle_inv_mass (array): Particle inverse mass, shape [particle_count], float
412
+ particle_radius (array): Particle radius, shape [particle_count], float
310
413
  particle_max_radius (float): Maximum particle radius (useful for HashGrid construction)
311
- particle_ke (wp.array): Particle normal contact stiffness (used by SemiImplicitIntegrator), shape [particle_count], float
312
- particle_kd (wp.array): Particle normal contact damping (used by SemiImplicitIntegrator), shape [particle_count], float
313
- particle_kf (wp.array): Particle friction force stiffness (used by SemiImplicitIntegrator), shape [particle_count], float
314
- particle_mu (wp.array): Particle friction coefficient, shape [particle_count], float
315
- particle_cohesion (wp.array): Particle cohesion strength, shape [particle_count], float
316
- particle_adhesion (wp.array): Particle adhesion strength, shape [particle_count], float
414
+ particle_ke (array): Particle normal contact stiffness (used by :class:`SemiImplicitIntegrator`), shape [particle_count], float
415
+ particle_kd (array): Particle normal contact damping (used by :class:`SemiImplicitIntegrator`), shape [particle_count], float
416
+ particle_kf (array): Particle friction force stiffness (used by :class:`SemiImplicitIntegrator`), shape [particle_count], float
417
+ particle_mu (array): Particle friction coefficient, shape [particle_count], float
418
+ particle_cohesion (array): Particle cohesion strength, shape [particle_count], float
419
+ particle_adhesion (array): Particle adhesion strength, shape [particle_count], float
317
420
  particle_grid (HashGrid): HashGrid instance used for accelerated simulation of particle interactions
318
- particle_flags (wp.array): Particle enabled state, shape [particle_count], bool
421
+ particle_flags (array): Particle enabled state, shape [particle_count], bool
422
+ particle_max_velocity (float): Maximum particle velocity (to prevent instability)
319
423
 
320
- shape_transform (wp.array): Rigid shape transforms, shape [shape_count, 7], float
321
- shape_body (wp.array): Rigid shape body index, shape [shape_count], int
424
+ shape_transform (array): Rigid shape transforms, shape [shape_count, 7], float
425
+ shape_body (array): Rigid shape body index, shape [shape_count], int
322
426
  body_shapes (dict): Mapping from body index to list of attached shape indices
323
427
  shape_materials (ModelShapeMaterials): Rigid shape contact materials, shape [shape_count], float
324
428
  shape_shape_geo (ModelShapeGeometry): Shape geometry properties (geo type, scale, thickness, etc.), shape [shape_count, 3], float
@@ -327,73 +431,72 @@ class Model:
327
431
  shape_collision_group (list): Collision group of each shape, shape [shape_count], int
328
432
  shape_collision_group_map (dict): Mapping from collision group to list of shape indices
329
433
  shape_collision_filter_pairs (set): Pairs of shape indices that should not collide
330
- shape_collision_radius (wp.array): Collision radius of each shape used for bounding sphere broadphase collision checking, shape [shape_count], float
434
+ shape_collision_radius (array): Collision radius of each shape used for bounding sphere broadphase collision checking, shape [shape_count], float
331
435
  shape_ground_collision (list): Indicates whether each shape should collide with the ground, shape [shape_count], bool
332
- shape_contact_pairs (wp.array): Pairs of shape indices that may collide, shape [contact_pair_count, 2], int
333
- shape_ground_contact_pairs (wp.array): Pairs of shape, ground indices that may collide, shape [ground_contact_pair_count, 2], int
334
-
335
- spring_indices (wp.array): Particle spring indices, shape [spring_count*2], int
336
- spring_rest_length (wp.array): Particle spring rest length, shape [spring_count], float
337
- spring_stiffness (wp.array): Particle spring stiffness, shape [spring_count], float
338
- spring_damping (wp.array): Particle spring damping, shape [spring_count], float
339
- spring_control (wp.array): Particle spring activation, shape [spring_count], float
340
-
341
- tri_indices (wp.array): Triangle element indices, shape [tri_count*3], int
342
- tri_poses (wp.array): Triangle element rest pose, shape [tri_count, 2, 2], float
343
- tri_activations (wp.array): Triangle element activations, shape [tri_count], float
344
- tri_materials (wp.array): Triangle element materials, shape [tri_count, 5], float
345
-
346
- edge_indices (wp.array): Bending edge indices, shape [edge_count*2], int
347
- edge_rest_angle (wp.array): Bending edge rest angle, shape [edge_count], float
348
- edge_bending_properties (wp.array): Bending edge stiffness and damping parameters, shape [edge_count, 2], float
349
-
350
- tet_indices (wp.array): Tetrahedral element indices, shape [tet_count*4], int
351
- tet_poses (wp.array): Tetrahedral rest poses, shape [tet_count, 3, 3], float
352
- tet_activations (wp.array): Tetrahedral volumetric activations, shape [tet_count], float
353
- tet_materials (wp.array): Tetrahedral elastic parameters in form :math:`k_{mu}, k_{lambda}, k_{damp}`, shape [tet_count, 3]
354
-
355
- body_q (wp.array): Poses of rigid bodies used for state initialization, shape [body_count, 7], float
356
- body_qd (wp.array): Velocities of rigid bodies used for state initialization, shape [body_count, 6], float
357
- body_com (wp.array): Rigid body center of mass (in local frame), shape [body_count, 7], float
358
- body_inertia (wp.array): Rigid body inertia tensor (relative to COM), shape [body_count, 3, 3], float
359
- body_inv_inertia (wp.array): Rigid body inverse inertia tensor (relative to COM), shape [body_count, 3, 3], float
360
- body_mass (wp.array): Rigid body mass, shape [body_count], float
361
- body_inv_mass (wp.array): Rigid body inverse mass, shape [body_count], float
436
+ shape_contact_pairs (array): Pairs of shape indices that may collide, shape [contact_pair_count, 2], int
437
+ shape_ground_contact_pairs (array): Pairs of shape, ground indices that may collide, shape [ground_contact_pair_count, 2], int
438
+
439
+ spring_indices (array): Particle spring indices, shape [spring_count*2], int
440
+ spring_rest_length (array): Particle spring rest length, shape [spring_count], float
441
+ spring_stiffness (array): Particle spring stiffness, shape [spring_count], float
442
+ spring_damping (array): Particle spring damping, shape [spring_count], float
443
+ spring_control (array): Particle spring activation, shape [spring_count], float
444
+
445
+ tri_indices (array): Triangle element indices, shape [tri_count*3], int
446
+ tri_poses (array): Triangle element rest pose, shape [tri_count, 2, 2], float
447
+ tri_activations (array): Triangle element activations, shape [tri_count], float
448
+ tri_materials (array): Triangle element materials, shape [tri_count, 5], float
449
+
450
+ edge_indices (array): Bending edge indices, shape [edge_count*4], int
451
+ edge_rest_angle (array): Bending edge rest angle, shape [edge_count], float
452
+ edge_bending_properties (array): Bending edge stiffness and damping parameters, shape [edge_count, 2], float
453
+
454
+ tet_indices (array): Tetrahedral element indices, shape [tet_count*4], int
455
+ tet_poses (array): Tetrahedral rest poses, shape [tet_count, 3, 3], float
456
+ tet_activations (array): Tetrahedral volumetric activations, shape [tet_count], float
457
+ tet_materials (array): Tetrahedral elastic parameters in form :math:`k_{mu}, k_{lambda}, k_{damp}`, shape [tet_count, 3]
458
+
459
+ body_q (array): Poses of rigid bodies used for state initialization, shape [body_count, 7], float
460
+ body_qd (array): Velocities of rigid bodies used for state initialization, shape [body_count, 6], float
461
+ body_com (array): Rigid body center of mass (in local frame), shape [body_count, 7], float
462
+ body_inertia (array): Rigid body inertia tensor (relative to COM), shape [body_count, 3, 3], float
463
+ body_inv_inertia (array): Rigid body inverse inertia tensor (relative to COM), shape [body_count, 3, 3], float
464
+ body_mass (array): Rigid body mass, shape [body_count], float
465
+ body_inv_mass (array): Rigid body inverse mass, shape [body_count], float
362
466
  body_name (list): Rigid body names, shape [body_count], str
363
467
 
364
- joint_q (wp.array): Generalized joint positions used for state initialization, shape [joint_coord_count], float
365
- joint_qd (wp.array): Generalized joint velocities used for state initialization, shape [joint_dof_count], float
366
- joint_act (wp.array): Generalized joint actuation force, shape [joint_dof_count], float
367
- joint_type (wp.array): Joint type, shape [joint_count], int
368
- joint_parent (wp.array): Joint parent body indices, shape [joint_count], int
369
- joint_child (wp.array): Joint child body indices, shape [joint_count], int
370
- joint_X_p (wp.array): Joint transform in parent frame, shape [joint_count, 7], float
371
- joint_X_c (wp.array): Joint mass frame in child frame, shape [joint_count, 7], float
372
- joint_axis (wp.array): Joint axis in child frame, shape [joint_axis_count, 3], float
373
- joint_armature (wp.array): Armature for each joint, shape [joint_count], float
374
- joint_target (wp.array): Joint target position/velocity (depending on joint axis mode), shape [joint_axis_count], float
375
- joint_target_ke (wp.array): Joint stiffness, shape [joint_axis_count], float
376
- joint_target_kd (wp.array): Joint damping, shape [joint_axis_count], float
377
- joint_axis_start (wp.array): Start index of the first axis per joint, shape [joint_count], int
378
- joint_axis_dim (wp.array): Number of linear and angular axes per joint, shape [joint_count, 2], int
379
- joint_axis_mode (wp.array): Joint axis mode, shape [joint_axis_count], int
380
- joint_linear_compliance (wp.array): Joint linear compliance, shape [joint_count], float
381
- joint_angular_compliance (wp.array): Joint linear compliance, shape [joint_count], float
382
- joint_enabled (wp.array): Joint enabled, shape [joint_count], int
383
- joint_limit_lower (wp.array): Joint lower position limits, shape [joint_count], float
384
- joint_limit_upper (wp.array): Joint upper position limits, shape [joint_count], float
385
- joint_limit_ke (wp.array): Joint position limit stiffness (used by SemiImplicitIntegrator), shape [joint_count], float
386
- joint_limit_kd (wp.array): Joint position limit damping (used by SemiImplicitIntegrator), shape [joint_count], float
387
- joint_twist_lower (wp.array): Joint lower twist limit, shape [joint_count], float
388
- joint_twist_upper (wp.array): Joint upper twist limit, shape [joint_count], float
389
- joint_q_start (wp.array): Start index of the first position coordinate per joint, shape [joint_count], int
390
- joint_qd_start (wp.array): Start index of the first velocity coordinate per joint, shape [joint_count], int
468
+ joint_q (array): Generalized joint positions used for state initialization, shape [joint_coord_count], float
469
+ joint_qd (array): Generalized joint velocities used for state initialization, shape [joint_dof_count], float
470
+ joint_act (array): Generalized joint actuation force, shape [joint_dof_count], float
471
+ joint_type (array): Joint type, shape [joint_count], int
472
+ joint_parent (array): Joint parent body indices, shape [joint_count], int
473
+ joint_child (array): Joint child body indices, shape [joint_count], int
474
+ joint_X_p (array): Joint transform in parent frame, shape [joint_count, 7], float
475
+ joint_X_c (array): Joint mass frame in child frame, shape [joint_count, 7], float
476
+ joint_axis (array): Joint axis in child frame, shape [joint_axis_count, 3], float
477
+ joint_armature (array): Armature for each joint, shape [joint_count], float
478
+ joint_target (array): Joint target position/velocity (depending on joint axis mode), shape [joint_axis_count], float
479
+ joint_target_ke (array): Joint stiffness, shape [joint_axis_count], float
480
+ joint_target_kd (array): Joint damping, shape [joint_axis_count], float
481
+ joint_axis_start (array): Start index of the first axis per joint, shape [joint_count], int
482
+ joint_axis_dim (array): Number of linear and angular axes per joint, shape [joint_count, 2], int
483
+ joint_axis_mode (array): Joint axis mode, shape [joint_axis_count], int
484
+ joint_linear_compliance (array): Joint linear compliance, shape [joint_count], float
485
+ joint_angular_compliance (array): Joint linear compliance, shape [joint_count], float
486
+ joint_enabled (array): Joint enabled, shape [joint_count], int
487
+ joint_limit_lower (array): Joint lower position limits, shape [joint_count], float
488
+ joint_limit_upper (array): Joint upper position limits, shape [joint_count], float
489
+ joint_limit_ke (array): Joint position limit stiffness (used by SemiImplicitIntegrator), shape [joint_count], float
490
+ joint_limit_kd (array): Joint position limit damping (used by SemiImplicitIntegrator), shape [joint_count], float
491
+ joint_twist_lower (array): Joint lower twist limit, shape [joint_count], float
492
+ joint_twist_upper (array): Joint upper twist limit, shape [joint_count], float
493
+ joint_q_start (array): Start index of the first position coordinate per joint, shape [joint_count], int
494
+ joint_qd_start (array): Start index of the first velocity coordinate per joint, shape [joint_count], int
495
+ articulation_start (array): Articulation start index, shape [articulation_count], int
391
496
  joint_name (list): Joint names, shape [joint_count], str
392
497
  joint_attach_ke (float): Joint attachment force stiffness (used by SemiImplicitIntegrator)
393
498
  joint_attach_kd (float): Joint attachment force damping (used by SemiImplicitIntegrator)
394
499
 
395
- articulation_start (wp.array): Articulation start index, shape [articulation_count], int
396
-
397
500
  soft_contact_margin (float): Contact margin for generation of soft contacts
398
501
  soft_contact_ke (float): Stiffness of soft contacts (used by SemiImplicitIntegrator)
399
502
  soft_contact_kd (float): Damping of soft contacts (used by SemiImplicitIntegrator)
@@ -406,7 +509,7 @@ class Model:
406
509
  rigid_contact_rolling_friction (float): Rolling friction coefficient for rigid body contacts (used by XPBDIntegrator)
407
510
 
408
511
  ground (bool): Whether the ground plane and ground contacts are enabled
409
- ground_plane (wp.array): Ground plane 3D normal and offset, shape [4], float
512
+ ground_plane (array): Ground plane 3D normal and offset, shape [4], float
410
513
  up_vector (np.ndarray): Up vector of the world, shape [3], float
411
514
  up_axis (int): Up axis, 0 for x, 1 for y, 2 for z
412
515
  gravity (np.ndarray): Gravity vector, shape [3], float
@@ -451,6 +554,7 @@ class Model:
451
554
  self.particle_adhesion = 0.0
452
555
  self.particle_grid = None
453
556
  self.particle_flags = None
557
+ self.particle_max_velocity = 1e5
454
558
 
455
559
  self.shape_transform = None
456
560
  self.shape_body = None
@@ -522,6 +626,8 @@ class Model:
522
626
  self.joint_twist_lower = None
523
627
  self.joint_twist_upper = None
524
628
  self.joint_q_start = None
629
+ self.joint_qd_start = None
630
+ self.articulation_start = None
525
631
  self.joint_name = None
526
632
 
527
633
  # todo: per-joint values?
@@ -562,7 +668,7 @@ class Model:
562
668
 
563
669
  self.device = wp.get_device(device)
564
670
 
565
- def state(self, requires_grad=False) -> State:
671
+ def state(self, requires_grad=None) -> State:
566
672
  """Returns a state object for the model
567
673
 
568
674
  The returned state will be initialized with the initial configuration given in
@@ -570,6 +676,8 @@ class Model:
570
676
  """
571
677
 
572
678
  s = State()
679
+ if requires_grad is None:
680
+ requires_grad = self.requires_grad
573
681
 
574
682
  s.particle_count = self.particle_count
575
683
  s.body_count = self.body_count
@@ -577,15 +685,6 @@ class Model:
577
685
  # --------------------------------
578
686
  # dynamic state (input, output)
579
687
 
580
- s.particle_q = None
581
- s.particle_qd = None
582
- s.particle_f = None
583
-
584
- s.body_q = None
585
- s.body_qd = None
586
- s.body_f = None
587
- s.body_deltas = None
588
-
589
688
  # particles
590
689
  if self.particle_count:
591
690
  s.particle_q = wp.clone(self.particle_q)
@@ -622,8 +721,8 @@ class Model:
622
721
 
623
722
  def find_shape_contact_pairs(self):
624
723
  # find potential contact pairs based on collision groups and collision mask (pairwise filtering)
625
- import itertools
626
724
  import copy
725
+ import itertools
627
726
 
628
727
  filters = copy.copy(self.shape_collision_filter_pairs)
629
728
  for a, b in self.shape_collision_filter_pairs:
@@ -775,9 +874,7 @@ class Model:
775
874
  return tensors
776
875
 
777
876
  def collide(self, state: State):
778
- import warnings
779
-
780
- warnings.warn(
877
+ wp.utils.warn(
781
878
  "Model.collide() is not needed anymore and will be removed in a future Warp version.",
782
879
  DeprecationWarning,
783
880
  stacklevel=2,
@@ -790,9 +887,7 @@ class Model:
790
887
 
791
888
  @property
792
889
  def soft_contact_distance(self):
793
- import warnings
794
-
795
- warnings.warn(
890
+ wp.utils.warn(
796
891
  "Model.soft_contact_distance is deprecated and will be removed in a future Warp version. "
797
892
  "Particles now have individual radii, returning `Model.particle_max_radius`.",
798
893
  DeprecationWarning,
@@ -802,9 +897,7 @@ class Model:
802
897
 
803
898
  @soft_contact_distance.setter
804
899
  def soft_contact_distance(self, value):
805
- import warnings
806
-
807
- warnings.warn(
900
+ wp.utils.warn(
808
901
  "Model.soft_contact_distance is deprecated and will be removed in a future Warp version. "
809
902
  "Particles now have individual radii, see `Model.particle_radius`.",
810
903
  DeprecationWarning,
@@ -813,18 +906,16 @@ class Model:
813
906
 
814
907
  @property
815
908
  def particle_radius(self):
816
- """Array of per-particle radii"""
909
+ # Array of per-particle radii
817
910
  return self._particle_radius
818
911
 
819
912
  @particle_radius.setter
820
913
  def particle_radius(self, value):
821
914
  if isinstance(value, float):
822
- import warnings
823
-
824
- warnings.warn(
915
+ wp.utils.warn(
825
916
  "Model.particle_radius is an array of per-particle radii, assigning with a scalar value "
826
917
  "is deprecated and will be removed in a future Warp version.",
827
- DeprecationWarning,
918
+ PendingDeprecationWarning,
828
919
  stacklevel=2,
829
920
  )
830
921
  self._particle_radius.fill_(value)
@@ -891,6 +982,10 @@ class ModelBuilder:
891
982
  default_tri_drag = 0.0
892
983
  default_tri_lift = 0.0
893
984
 
985
+ # Default distance constraint properties
986
+ default_spring_ke = 100.0
987
+ default_spring_kd = 0.0
988
+
894
989
  # Default edge bending properties
895
990
  default_edge_ke = 100.0
896
991
  default_edge_kd = 0.0
@@ -919,6 +1014,7 @@ class ModelBuilder:
919
1014
  self.particle_mass = []
920
1015
  self.particle_radius = []
921
1016
  self.particle_flags = []
1017
+ self.particle_max_velocity = 1e5
922
1018
 
923
1019
  # shapes (each shape has an entry in these arrays)
924
1020
  # transform from shape to body
@@ -1034,8 +1130,8 @@ class ModelBuilder:
1034
1130
  self.joint_coord_count = 0
1035
1131
  self.joint_axis_total_count = 0
1036
1132
 
1037
- self.up_vector = up_vector
1038
- self.up_axis = np.argmax(np.abs(up_vector))
1133
+ self.up_vector = wp.vec3(up_vector)
1134
+ self.up_axis = wp.vec3(np.argmax(np.abs(up_vector)))
1039
1135
  self.gravity = gravity
1040
1136
  # indicates whether a ground plane has been created
1041
1137
  self._ground_created = False
@@ -1126,15 +1222,25 @@ class ModelBuilder:
1126
1222
  """Copies a rigid articulation from `articulation`, another `ModelBuilder`.
1127
1223
 
1128
1224
  Args:
1129
- articulation: a model builder to add rigid articulation from.
1130
- xform: offset transform applied to root bodies.
1131
- update_num_env_count: if True, the number of environments is incremented by 1.
1132
- separate_collision_group: if True, the shapes from the articulation will all be put into a single new collision group, otherwise, only the shapes in collision group > -1 will be moved to a new group.
1225
+ articulation (ModelBuilder): a model builder to add rigid articulation from.
1226
+ xform (:ref:`transform <transform>`): offset transform applied to root bodies.
1227
+ update_num_env_count (bool): if True, the number of environments is incremented by 1.
1228
+ separate_collision_group (bool): if True, the shapes from the articulation will all be put into a single new collision group, otherwise, only the shapes in collision group > -1 will be moved to a new group.
1133
1229
  """
1134
1230
 
1135
1231
  start_body_idx = self.body_count
1136
1232
  start_shape_idx = self.shape_count
1137
- self.shape_body.extend([b + start_body_idx for b in articulation.shape_body])
1233
+ for s, b in enumerate(articulation.shape_body):
1234
+ if b > -1:
1235
+ new_b = b + start_body_idx
1236
+ self.shape_body.append(new_b)
1237
+ self.shape_transform.append(articulation.shape_transform[s])
1238
+ else:
1239
+ self.shape_body.append(-1)
1240
+ # apply offset transform to root bodies
1241
+ if xform is not None:
1242
+ self.shape_transform.append(xform * articulation.shape_transform[s])
1243
+
1138
1244
  for b, shapes in articulation.body_shapes.items():
1139
1245
  self.body_shapes[b + start_body_idx] = [s + start_shape_idx for s in shapes]
1140
1246
 
@@ -1227,7 +1333,6 @@ class ModelBuilder:
1227
1333
  "joint_target_kd",
1228
1334
  "joint_linear_compliance",
1229
1335
  "joint_angular_compliance",
1230
- "shape_transform",
1231
1336
  "shape_geo_type",
1232
1337
  "shape_geo_scale",
1233
1338
  "shape_geo_src",
@@ -1260,8 +1365,8 @@ class ModelBuilder:
1260
1365
  self,
1261
1366
  origin: Transform = wp.transform(),
1262
1367
  armature: float = 0.0,
1263
- com: Vec3 = np.zeros(3),
1264
- I_m: Mat33 = np.zeros((3, 3)),
1368
+ com: Vec3 = wp.vec3(),
1369
+ I_m: Mat33 = wp.mat33(),
1265
1370
  m: float = 0.0,
1266
1371
  name: str = None,
1267
1372
  ) -> int:
@@ -1286,7 +1391,7 @@ class ModelBuilder:
1286
1391
  body_id = len(self.body_mass)
1287
1392
 
1288
1393
  # body data
1289
- inertia = I_m + np.eye(3) * armature
1394
+ inertia = I_m + wp.mat33(np.eye(3)) * armature
1290
1395
  self.body_inertia.append(inertia)
1291
1396
  self.body_mass.append(m)
1292
1397
  self.body_com.append(com)
@@ -1296,8 +1401,8 @@ class ModelBuilder:
1296
1401
  else:
1297
1402
  self.body_inv_mass.append(0.0)
1298
1403
 
1299
- if inertia.any():
1300
- self.body_inv_inertia.append(np.linalg.inv(inertia))
1404
+ if any(x for x in inertia):
1405
+ self.body_inv_inertia.append(wp.inverse(inertia))
1301
1406
  else:
1302
1407
  self.body_inv_inertia.append(inertia)
1303
1408
 
@@ -1323,6 +1428,29 @@ class ModelBuilder:
1323
1428
  collision_filter_parent: bool = True,
1324
1429
  enabled: bool = True,
1325
1430
  ) -> int:
1431
+ """
1432
+ Generic method to add any type of joint to this ModelBuilder.
1433
+
1434
+ Args:
1435
+ joint_type: The type of joint to add (see `Joint types`_)
1436
+ parent: The index of the parent body (-1 is the world)
1437
+ child: The index of the child body
1438
+ linear_axes: The linear axes (see :class:`JointAxis`) of the joint
1439
+ angular_axes: The angular axes (see :class:`JointAxis`) of the joint
1440
+ name: The name of the joint
1441
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1442
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1443
+ linear_compliance: The linear compliance of the joint
1444
+ angular_compliance: The angular compliance of the joint
1445
+ collision_filter_parent: Whether to filter collisions between shapes of the parent and child bodies
1446
+ enabled: Whether the joint is enabled
1447
+
1448
+ Returns:
1449
+ The index of the added joint
1450
+ """
1451
+ if len(self.articulation_start) == 0:
1452
+ # automatically add an articulation if none exists
1453
+ self.add_articulation()
1326
1454
  self.joint_type.append(joint_type)
1327
1455
  self.joint_parent.append(parent)
1328
1456
  if child not in self.joint_parents:
@@ -1330,8 +1458,8 @@ class ModelBuilder:
1330
1458
  else:
1331
1459
  self.joint_parents[child].append(parent)
1332
1460
  self.joint_child.append(child)
1333
- self.joint_X_p.append([*parent_xform])
1334
- self.joint_X_c.append([*child_xform])
1461
+ self.joint_X_p.append(wp.transform(parent_xform))
1462
+ self.joint_X_c.append(wp.transform(child_xform))
1335
1463
  self.joint_name.append(name or f"joint {self.joint_count}")
1336
1464
  self.joint_axis_start.append(len(self.joint_axis))
1337
1465
  self.joint_axis_dim.append((len(linear_axes), len(angular_axes)))
@@ -1341,7 +1469,7 @@ class ModelBuilder:
1341
1469
  self.joint_angular_compliance.append(angular_compliance)
1342
1470
  self.joint_enabled.append(enabled)
1343
1471
 
1344
- for dim in linear_axes:
1472
+ def add_axis_dim(dim):
1345
1473
  self.joint_axis.append(dim.axis)
1346
1474
  self.joint_axis_mode.append(dim.mode)
1347
1475
  self.joint_target.append(dim.target)
@@ -1349,18 +1477,21 @@ class ModelBuilder:
1349
1477
  self.joint_target_kd.append(dim.target_kd)
1350
1478
  self.joint_limit_ke.append(dim.limit_ke)
1351
1479
  self.joint_limit_kd.append(dim.limit_kd)
1352
- self.joint_limit_lower.append(dim.limit_lower)
1353
- self.joint_limit_upper.append(dim.limit_upper)
1480
+ if np.isfinite(dim.limit_lower):
1481
+ self.joint_limit_lower.append(dim.limit_lower)
1482
+ else:
1483
+ self.joint_limit_lower.append(-1e6)
1484
+ if np.isfinite(dim.limit_upper):
1485
+ self.joint_limit_upper.append(dim.limit_upper)
1486
+ else:
1487
+ self.joint_limit_upper.append(1e6)
1488
+ # self.joint_limit_lower.append(dim.limit_lower)
1489
+ # self.joint_limit_upper.append(dim.limit_upper)
1490
+
1491
+ for dim in linear_axes:
1492
+ add_axis_dim(dim)
1354
1493
  for dim in angular_axes:
1355
- self.joint_axis.append(dim.axis)
1356
- self.joint_axis_mode.append(dim.mode)
1357
- self.joint_target.append(dim.target)
1358
- self.joint_target_ke.append(dim.target_ke)
1359
- self.joint_target_kd.append(dim.target_kd)
1360
- self.joint_limit_ke.append(dim.limit_ke)
1361
- self.joint_limit_kd.append(dim.limit_kd)
1362
- self.joint_limit_lower.append(dim.limit_lower)
1363
- self.joint_limit_upper.append(dim.limit_upper)
1494
+ add_axis_dim(dim)
1364
1495
 
1365
1496
  if joint_type == JOINT_PRISMATIC:
1366
1497
  dof_count = 1
@@ -1371,7 +1502,7 @@ class ModelBuilder:
1371
1502
  elif joint_type == JOINT_BALL:
1372
1503
  dof_count = 3
1373
1504
  coord_count = 4
1374
- elif joint_type == JOINT_FREE:
1505
+ elif joint_type == JOINT_FREE or joint_type == JOINT_DISTANCE:
1375
1506
  dof_count = 6
1376
1507
  coord_count = 7
1377
1508
  elif joint_type == JOINT_FIXED:
@@ -1383,10 +1514,6 @@ class ModelBuilder:
1383
1514
  elif joint_type == JOINT_COMPOUND:
1384
1515
  dof_count = 3
1385
1516
  coord_count = 3
1386
- elif joint_type == JOINT_DISTANCE:
1387
- # todo use free joint dofs?
1388
- dof_count = 0
1389
- coord_count = 0
1390
1517
  elif joint_type == JOINT_D6:
1391
1518
  dof_count = len(linear_axes) + len(angular_axes)
1392
1519
  coord_count = dof_count
@@ -1398,7 +1525,7 @@ class ModelBuilder:
1398
1525
  self.joint_qd.append(0.0)
1399
1526
  self.joint_act.append(0.0)
1400
1527
 
1401
- if joint_type == JOINT_FREE or joint_type == JOINT_BALL:
1528
+ if joint_type == JOINT_FREE or joint_type == JOINT_DISTANCE or joint_type == JOINT_BALL:
1402
1529
  # ensure that a valid quaternion is used for the angular dofs
1403
1530
  self.joint_q[-1] = 1.0
1404
1531
 
@@ -1436,14 +1563,14 @@ class ModelBuilder:
1436
1563
  collision_filter_parent: bool = True,
1437
1564
  enabled: bool = True,
1438
1565
  ) -> int:
1439
- """Adds a revolute joint to the model
1566
+ """Adds a revolute (hinge) joint to the model. It has one degree of freedom.
1440
1567
 
1441
1568
  Args:
1442
1569
  parent: The index of the parent body
1443
1570
  child: The index of the child body
1444
- parent_xform: The transform of the joint in the parent body's local frame
1445
- child_xform: The transform of the joint in the child body's local frame
1446
- axis: The axis of rotation in the parent body's local frame
1571
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1572
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1573
+ axis (3D vector or JointAxis): The axis of rotation in the parent body's local frame, can be a JointAxis object whose settings will be used instead of the other arguments
1447
1574
  target: The target angle (in radians) of the joint
1448
1575
  target_ke: The stiffness of the joint target
1449
1576
  target_kd: The damping of the joint target
@@ -1458,7 +1585,7 @@ class ModelBuilder:
1458
1585
  enabled: Whether the joint is enabled
1459
1586
 
1460
1587
  Returns:
1461
- The index of the child body
1588
+ The index of the added joint
1462
1589
 
1463
1590
  """
1464
1591
  ax = JointAxis(
@@ -1507,14 +1634,14 @@ class ModelBuilder:
1507
1634
  collision_filter_parent: bool = True,
1508
1635
  enabled: bool = True,
1509
1636
  ) -> int:
1510
- """Adds a prismatic joint to the model
1637
+ """Adds a prismatic (sliding) joint to the model. It has one degree of freedom.
1511
1638
 
1512
1639
  Args:
1513
1640
  parent: The index of the parent body
1514
1641
  child: The index of the child body
1515
- parent_xform: The transform of the joint in the parent body's local frame
1516
- child_xform: The transform of the joint in the child body's local frame
1517
- axis: The axis of rotation in the parent body's local frame
1642
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1643
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1644
+ axis (3D vector or JointAxis): The axis of rotation in the parent body's local frame, can be a JointAxis object whose settings will be used instead of the other arguments
1518
1645
  target: The target position of the joint
1519
1646
  target_ke: The stiffness of the joint target
1520
1647
  target_kd: The damping of the joint target
@@ -1529,7 +1656,7 @@ class ModelBuilder:
1529
1656
  enabled: Whether the joint is enabled
1530
1657
 
1531
1658
  Returns:
1532
- The index of the child body
1659
+ The index of the added joint
1533
1660
 
1534
1661
  """
1535
1662
  ax = JointAxis(
@@ -1569,13 +1696,13 @@ class ModelBuilder:
1569
1696
  collision_filter_parent: bool = True,
1570
1697
  enabled: bool = True,
1571
1698
  ) -> int:
1572
- """Adds a ball joint to the model
1699
+ """Adds a ball (spherical) joint to the model. Its position is defined by a 4D quaternion (xyzw) and its velocity is a 3D vector.
1573
1700
 
1574
1701
  Args:
1575
1702
  parent: The index of the parent body
1576
1703
  child: The index of the child body
1577
- parent_xform: The transform of the joint in the parent body's local frame
1578
- xform: The transform of the joint in the child body's local frame
1704
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1705
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1579
1706
  linear_compliance: The linear compliance of the joint
1580
1707
  angular_compliance: The angular compliance of the joint
1581
1708
  name: The name of the joint
@@ -1583,7 +1710,7 @@ class ModelBuilder:
1583
1710
  enabled: Whether the joint is enabled
1584
1711
 
1585
1712
  Returns:
1586
- The index of the child body
1713
+ The index of the added joint
1587
1714
 
1588
1715
  """
1589
1716
  return self.add_joint(
@@ -1611,13 +1738,14 @@ class ModelBuilder:
1611
1738
  collision_filter_parent: bool = True,
1612
1739
  enabled: bool = True,
1613
1740
  ) -> int:
1614
- """Adds a fixed joint to the model
1741
+ """Adds a fixed (static) joint to the model. It has no degrees of freedom.
1742
+ See :meth:`collapse_fixed_joints` for a helper function that removes these fixed joints and merges the connecting bodies to simplify the model and improve stability.
1615
1743
 
1616
1744
  Args:
1617
1745
  parent: The index of the parent body
1618
1746
  child: The index of the child body
1619
- parent_xform: The transform of the joint in the parent body's local frame
1620
- xform: The transform of the joint in the child body's local frame
1747
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1748
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1621
1749
  linear_compliance: The linear compliance of the joint
1622
1750
  angular_compliance: The angular compliance of the joint
1623
1751
  name: The name of the joint
@@ -1625,7 +1753,7 @@ class ModelBuilder:
1625
1753
  enabled: Whether the joint is enabled
1626
1754
 
1627
1755
  Returns:
1628
- The index of the child body
1756
+ The index of the added joint
1629
1757
 
1630
1758
  """
1631
1759
  return self.add_joint(
@@ -1651,19 +1779,20 @@ class ModelBuilder:
1651
1779
  collision_filter_parent: bool = True,
1652
1780
  enabled: bool = True,
1653
1781
  ) -> int:
1654
- """Adds a free joint to the model
1782
+ """Adds a free joint to the model.
1783
+ It has 7 positional degrees of freedom (first 3 linear and then 4 angular dimensions for the orientation quaternion in `xyzw` notation) and 6 velocity degrees of freedom (first 3 angular and then 3 linear velocity dimensions).
1655
1784
 
1656
1785
  Args:
1657
- parent: The index of the parent body
1658
1786
  child: The index of the child body
1659
- parent_xform: The transform of the joint in the parent body's local frame
1660
- xform: The transform of the joint in the child body's local frame
1787
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1788
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1789
+ parent: The index of the parent body (-1 by default to use the world frame, e.g. to make the child body and its children a floating-base mechanism)
1661
1790
  name: The name of the joint
1662
1791
  collision_filter_parent: Whether to filter collisions between shapes of the parent and child bodies
1663
1792
  enabled: Whether the joint is enabled
1664
1793
 
1665
1794
  Returns:
1666
- The index of the child body
1795
+ The index of the added joint
1667
1796
 
1668
1797
  """
1669
1798
  return self.add_joint(
@@ -1689,13 +1818,14 @@ class ModelBuilder:
1689
1818
  collision_filter_parent: bool = True,
1690
1819
  enabled: bool = True,
1691
1820
  ) -> int:
1692
- """Adds a distance joint to the model
1821
+ """Adds a distance joint to the model. The distance joint constraints the distance between the joint anchor points on the two bodies (see :ref:`FK-IK`) it connects to the interval [`min_distance`, `max_distance`].
1822
+ It has 7 positional degrees of freedom (first 3 linear and then 4 angular dimensions for the orientation quaternion in `xyzw` notation) and 6 velocity degrees of freedom (first 3 angular and then 3 linear velocity dimensions).
1693
1823
 
1694
1824
  Args:
1695
1825
  parent: The index of the parent body
1696
1826
  child: The index of the child body
1697
- parent_xform: The transform of the joint in the parent body's local frame
1698
- xform: The transform of the joint in the child body's local frame
1827
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1828
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1699
1829
  min_distance: The minimum distance between the bodies (no limit if negative)
1700
1830
  max_distance: The maximum distance between the bodies (no limit if negative)
1701
1831
  compliance: The compliance of the joint
@@ -1703,7 +1833,9 @@ class ModelBuilder:
1703
1833
  enabled: Whether the joint is enabled
1704
1834
 
1705
1835
  Returns:
1706
- The index of the child body
1836
+ The index of the added joint
1837
+
1838
+ .. note:: Distance joints are currently only supported in the :class:`XPBDIntegrator` at the moment.
1707
1839
 
1708
1840
  """
1709
1841
  ax = JointAxis(
@@ -1737,15 +1869,15 @@ class ModelBuilder:
1737
1869
  collision_filter_parent: bool = True,
1738
1870
  enabled: bool = True,
1739
1871
  ) -> int:
1740
- """Adds a universal joint to the model
1872
+ """Adds a universal joint to the model. U-joints have two degrees of freedom, one for each axis.
1741
1873
 
1742
1874
  Args:
1743
1875
  parent: The index of the parent body
1744
1876
  child: The index of the child body
1745
- axis_0: The first axis of the joint
1746
- axis_1: The second axis of the joint
1747
- parent_xform: The transform of the joint in the parent body's local frame
1748
- xform: The transform of the joint in the child body's local frame
1877
+ axis_0 (3D vector or JointAxis): The first axis of the joint, can be a JointAxis object whose settings will be used instead of the other arguments
1878
+ axis_1 (3D vector or JointAxis): The second axis of the joint, can be a JointAxis object whose settings will be used instead of the other arguments
1879
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1880
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1749
1881
  linear_compliance: The linear compliance of the joint
1750
1882
  angular_compliance: The angular compliance of the joint
1751
1883
  name: The name of the joint
@@ -1753,14 +1885,14 @@ class ModelBuilder:
1753
1885
  enabled: Whether the joint is enabled
1754
1886
 
1755
1887
  Returns:
1756
- The index of the child body
1888
+ The index of the added joint
1757
1889
 
1758
1890
  """
1759
1891
  return self.add_joint(
1760
1892
  JOINT_UNIVERSAL,
1761
1893
  parent,
1762
1894
  child,
1763
- angular_axes=[axis_0, axis_1],
1895
+ angular_axes=[JointAxis(axis_0), JointAxis(axis_1)],
1764
1896
  parent_xform=parent_xform,
1765
1897
  child_xform=child_xform,
1766
1898
  linear_compliance=linear_compliance,
@@ -1783,29 +1915,32 @@ class ModelBuilder:
1783
1915
  collision_filter_parent: bool = True,
1784
1916
  enabled: bool = True,
1785
1917
  ) -> int:
1786
- """Adds a compound joint to the model
1918
+ """Adds a compound joint to the model, which has 3 degrees of freedom, one for each axis.
1919
+ Similar to the ball joint (see :meth:`add_ball_joint`), the compound joint allows bodies to move in a 3D rotation relative to each other,
1920
+ except that the rotation is defined by 3 axes instead of a quaternion.
1921
+ Depending on the choice of axes, the orientation can be specified through Euler angles, e.g. `z-x-z` or `x-y-x`, or through a Tait-Bryan angle sequence, e.g. `z-y-x` or `x-y-z`.
1787
1922
 
1788
1923
  Args:
1789
1924
  parent: The index of the parent body
1790
1925
  child: The index of the child body
1791
- axis_0: The first axis of the joint
1792
- axis_1: The second axis of the joint
1793
- axis_2: The third axis of the joint
1794
- parent_xform: The transform of the joint in the parent body's local frame
1795
- xform: The transform of the joint in the child body's local frame
1926
+ axis_0 (3D vector or JointAxis): The first axis of the joint, can be a JointAxis object whose settings will be used instead of the other arguments
1927
+ axis_1 (3D vector or JointAxis): The second axis of the joint, can be a JointAxis object whose settings will be used instead of the other arguments
1928
+ axis_2 (3D vector or JointAxis): The third axis of the joint, can be a JointAxis object whose settings will be used instead of the other arguments
1929
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1930
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1796
1931
  name: The name of the joint
1797
1932
  collision_filter_parent: Whether to filter collisions between shapes of the parent and child bodies
1798
1933
  enabled: Whether the joint is enabled
1799
1934
 
1800
1935
  Returns:
1801
- The index of the child body
1936
+ The index of the added joint
1802
1937
 
1803
1938
  """
1804
1939
  return self.add_joint(
1805
1940
  JOINT_COMPOUND,
1806
1941
  parent,
1807
1942
  child,
1808
- angular_axes=[axis_0, axis_1, axis_2],
1943
+ angular_axes=[JointAxis(axis_0), JointAxis(axis_1), JointAxis(axis_2)],
1809
1944
  parent_xform=parent_xform,
1810
1945
  child_xform=child_xform,
1811
1946
  name=name,
@@ -1827,7 +1962,7 @@ class ModelBuilder:
1827
1962
  collision_filter_parent: bool = True,
1828
1963
  enabled: bool = True,
1829
1964
  ):
1830
- """Adds a generic joint with custom linear and angular axes.
1965
+ """Adds a generic joint with custom linear and angular axes. The number of axes determines the number of degrees of freedom of the joint.
1831
1966
 
1832
1967
  Args:
1833
1968
  parent: The index of the parent body
@@ -1835,15 +1970,15 @@ class ModelBuilder:
1835
1970
  linear_axes: A list of linear axes
1836
1971
  angular_axes: A list of angular axes
1837
1972
  name: The name of the joint
1838
- parent_xform: The transform of the joint in the parent body's local frame
1839
- xform: The transform of the joint in the child body's local frame
1973
+ parent_xform (:ref:`transform <transform>`): The transform of the joint in the parent body's local frame
1974
+ child_xform (:ref:`transform <transform>`): The transform of the joint in the child body's local frame
1840
1975
  linear_compliance: The linear compliance of the joint
1841
1976
  angular_compliance: The angular compliance of the joint
1842
1977
  collision_filter_parent: Whether to filter collisions between shapes of the parent and child bodies
1843
1978
  enabled: Whether the joint is enabled
1844
1979
 
1845
1980
  Returns:
1846
- The index of the child body
1981
+ The index of the added joint
1847
1982
 
1848
1983
  """
1849
1984
  return self.add_joint(
@@ -1852,8 +1987,8 @@ class ModelBuilder:
1852
1987
  child,
1853
1988
  parent_xform=parent_xform,
1854
1989
  child_xform=child_xform,
1855
- linear_axes=linear_axes,
1856
- angular_axes=angular_axes,
1990
+ linear_axes=[JointAxis(a) for a in linear_axes],
1991
+ angular_axes=[JointAxis(a) for a in angular_axes],
1857
1992
  linear_compliance=linear_compliance,
1858
1993
  angular_compliance=angular_compliance,
1859
1994
  name=name,
@@ -1861,11 +1996,257 @@ class ModelBuilder:
1861
1996
  enabled=enabled,
1862
1997
  )
1863
1998
 
1999
+ def collapse_fixed_joints(self, verbose=wp.config.verbose):
2000
+ """Removes fixed joints from the model and merges the bodies they connect. This is useful for simplifying the model for faster and more stable simulation."""
2001
+
2002
+ body_data = {}
2003
+ body_children = {-1: []}
2004
+ visited = {}
2005
+ for i in range(self.body_count):
2006
+ name = self.body_name[i]
2007
+ body_data[i] = {
2008
+ "shapes": self.body_shapes[i],
2009
+ "q": self.body_q[i],
2010
+ "qd": self.body_qd[i],
2011
+ "mass": self.body_mass[i],
2012
+ "inertia": self.body_inertia[i],
2013
+ "inv_mass": self.body_inv_mass[i],
2014
+ "inv_inertia": self.body_inv_inertia[i],
2015
+ "com": self.body_com[i],
2016
+ "name": name,
2017
+ "original_id": i,
2018
+ }
2019
+ visited[i] = False
2020
+ body_children[i] = []
2021
+
2022
+ joint_data = {}
2023
+ for i in range(self.joint_count):
2024
+ name = self.joint_name[i]
2025
+ parent = self.joint_parent[i]
2026
+ child = self.joint_child[i]
2027
+ body_children[parent].append(child)
2028
+
2029
+ q_start = self.joint_q_start[i]
2030
+ qd_start = self.joint_qd_start[i]
2031
+ if i < self.joint_count - 1:
2032
+ q_dim = self.joint_q_start[i + 1] - q_start
2033
+ qd_dim = self.joint_qd_start[i + 1] - qd_start
2034
+ else:
2035
+ q_dim = len(self.joint_q) - q_start
2036
+ qd_dim = len(self.joint_qd) - qd_start
2037
+
2038
+ data = {
2039
+ "type": self.joint_type[i],
2040
+ # 'armature': self.joint_armature[i],
2041
+ "q": self.joint_q[q_start : q_start + q_dim],
2042
+ "qd": self.joint_qd[qd_start : qd_start + qd_dim],
2043
+ "act": self.joint_act[qd_start : qd_start + qd_dim],
2044
+ "q_start": q_start,
2045
+ "qd_start": qd_start,
2046
+ "linear_compliance": self.joint_linear_compliance[i],
2047
+ "angular_compliance": self.joint_angular_compliance[i],
2048
+ "name": name,
2049
+ "parent_xform": wp.transform_expand(self.joint_X_p[i]),
2050
+ "child_xform": wp.transform_expand(self.joint_X_c[i]),
2051
+ "enabled": self.joint_enabled[i],
2052
+ "axes": [],
2053
+ "axis_dim": self.joint_axis_dim[i],
2054
+ "parent": parent,
2055
+ "child": child,
2056
+ "original_id": i,
2057
+ }
2058
+ num_lin_axes, num_ang_axes = self.joint_axis_dim[i]
2059
+ start_ax = self.joint_axis_start[i]
2060
+ for j in range(start_ax, start_ax + num_lin_axes + num_ang_axes):
2061
+ data["axes"].append(
2062
+ {
2063
+ "axis": self.joint_axis[j],
2064
+ "axis_mode": self.joint_axis_mode[j],
2065
+ "target": self.joint_target[j],
2066
+ "target_ke": self.joint_target_ke[j],
2067
+ "target_kd": self.joint_target_kd[j],
2068
+ "limit_ke": self.joint_limit_ke[j],
2069
+ "limit_kd": self.joint_limit_kd[j],
2070
+ "limit_lower": self.joint_limit_lower[j],
2071
+ "limit_upper": self.joint_limit_upper[j],
2072
+ }
2073
+ )
2074
+
2075
+ joint_data[(parent, child)] = data
2076
+
2077
+ # sort body children so we traverse the tree in the same order as the bodies are listed
2078
+ for children in body_children.values():
2079
+ children.sort(key=lambda x: body_data[x]["original_id"])
2080
+
2081
+ retained_joints = []
2082
+ retained_bodies = []
2083
+ body_remap = {-1: -1}
2084
+
2085
+ # depth first search over the joint graph
2086
+ def dfs(parent_body: int, child_body: int, incoming_xform: wp.transform, last_dynamic_body: int):
2087
+ nonlocal visited
2088
+ nonlocal retained_joints
2089
+ nonlocal retained_bodies
2090
+ nonlocal body_data
2091
+ nonlocal body_remap
2092
+
2093
+ joint = joint_data[(parent_body, child_body)]
2094
+ if joint["type"] == JOINT_FIXED:
2095
+ joint_xform = joint["parent_xform"] * wp.transform_inverse(joint["child_xform"])
2096
+ incoming_xform = incoming_xform * joint_xform
2097
+ parent_name = self.body_name[parent_body] if parent_body > -1 else "world"
2098
+ child_name = self.body_name[child_body]
2099
+ last_dynamic_body_name = self.body_name[last_dynamic_body] if last_dynamic_body > -1 else "world"
2100
+ if verbose:
2101
+ print(
2102
+ f'Remove fixed joint {joint["name"]} between {parent_name} and {child_name}, '
2103
+ f"merging {child_name} into {last_dynamic_body_name}"
2104
+ )
2105
+ child_id = body_data[child_body]["original_id"]
2106
+ for shape in self.body_shapes[child_id]:
2107
+ self.shape_transform[shape] = incoming_xform * self.shape_transform[shape]
2108
+ if verbose:
2109
+ print(
2110
+ f" Shape {shape} moved to body {last_dynamic_body_name} with transform {self.shape_transform[shape]}"
2111
+ )
2112
+ if last_dynamic_body > -1:
2113
+ self.shape_body[shape] = body_data[last_dynamic_body]["id"]
2114
+ # add inertia to last_dynamic_body
2115
+ m = body_data[child_body]["mass"]
2116
+ com = body_data[child_body]["com"]
2117
+ inertia = body_data[child_body]["inertia"]
2118
+ body_data[last_dynamic_body]["inertia"] += wp.sim.transform_inertia(
2119
+ m, inertia, incoming_xform.p, incoming_xform.q
2120
+ )
2121
+ body_data[last_dynamic_body]["mass"] += m
2122
+ source_m = body_data[last_dynamic_body]["mass"]
2123
+ source_com = body_data[last_dynamic_body]["com"]
2124
+ body_data[last_dynamic_body]["com"] = (m * com + source_m * source_com) / (m + source_m)
2125
+ body_data[last_dynamic_body]["shapes"].append(shape)
2126
+ # indicate to recompute inverse mass, inertia for this body
2127
+ body_data[last_dynamic_body]["inv_mass"] = None
2128
+ else:
2129
+ self.shape_body[shape] = -1
2130
+ else:
2131
+ joint["parent_xform"] = incoming_xform * joint["parent_xform"]
2132
+ joint["parent"] = last_dynamic_body
2133
+ last_dynamic_body = child_body
2134
+ incoming_xform = wp.transform()
2135
+ retained_joints.append(joint)
2136
+ new_id = len(retained_bodies)
2137
+ body_data[child_body]["id"] = new_id
2138
+ retained_bodies.append(child_body)
2139
+ for shape in body_data[child_body]["shapes"]:
2140
+ self.shape_body[shape] = new_id
2141
+
2142
+ visited[parent_body] = True
2143
+ if visited[child_body] or child_body not in body_children:
2144
+ return
2145
+ for child in body_children[child_body]:
2146
+ if not visited[child]:
2147
+ dfs(child_body, child, incoming_xform, last_dynamic_body)
2148
+
2149
+ for body in body_children[-1]:
2150
+ if not visited[body]:
2151
+ dfs(-1, body, wp.transform(), -1)
2152
+
2153
+ # repopulate the model
2154
+ self.body_name.clear()
2155
+ self.body_q.clear()
2156
+ self.body_qd.clear()
2157
+ self.body_mass.clear()
2158
+ self.body_inertia.clear()
2159
+ self.body_com.clear()
2160
+ self.body_inv_mass.clear()
2161
+ self.body_inv_inertia.clear()
2162
+ self.body_shapes.clear()
2163
+ for i in retained_bodies:
2164
+ body = body_data[i]
2165
+ new_id = len(self.body_name)
2166
+ body_remap[body["original_id"]] = new_id
2167
+ self.body_name.append(body["name"])
2168
+ self.body_q.append(list(body["q"]))
2169
+ self.body_qd.append(list(body["qd"]))
2170
+ m = body["mass"]
2171
+ inertia = body["inertia"]
2172
+ self.body_mass.append(m)
2173
+ self.body_inertia.append(inertia)
2174
+ self.body_com.append(body["com"])
2175
+ if body["inv_mass"] is None:
2176
+ # recompute inverse mass and inertia
2177
+ if m > 0.0:
2178
+ self.body_inv_mass.append(1.0 / m)
2179
+ self.body_inv_inertia.append(np.linalg.inv(inertia))
2180
+ else:
2181
+ self.body_inv_mass.append(0.0)
2182
+ self.body_inv_inertia.append(np.zeros((3, 3)))
2183
+ else:
2184
+ self.body_inv_mass.append(body["inv_mass"])
2185
+ self.body_inv_inertia.append(body["inv_inertia"])
2186
+ self.body_shapes[new_id] = body["shapes"]
2187
+ body_remap[body["original_id"]] = new_id
2188
+
2189
+ # sort joints so they appear in the same order as before
2190
+ retained_joints.sort(key=lambda x: x["original_id"])
2191
+
2192
+ self.joint_name.clear()
2193
+ self.joint_type.clear()
2194
+ self.joint_parent.clear()
2195
+ self.joint_child.clear()
2196
+ self.joint_q.clear()
2197
+ self.joint_qd.clear()
2198
+ self.joint_q_start.clear()
2199
+ self.joint_qd_start.clear()
2200
+ self.joint_enabled.clear()
2201
+ self.joint_linear_compliance.clear()
2202
+ self.joint_angular_compliance.clear()
2203
+ self.joint_X_p.clear()
2204
+ self.joint_X_c.clear()
2205
+ self.joint_axis.clear()
2206
+ self.joint_axis_mode.clear()
2207
+ self.joint_target.clear()
2208
+ self.joint_target_ke.clear()
2209
+ self.joint_target_kd.clear()
2210
+ self.joint_limit_lower.clear()
2211
+ self.joint_limit_upper.clear()
2212
+ self.joint_limit_ke.clear()
2213
+ self.joint_limit_kd.clear()
2214
+ self.joint_axis_dim.clear()
2215
+ self.joint_axis_start.clear()
2216
+ self.joint_act.clear()
2217
+ for joint in retained_joints:
2218
+ self.joint_name.append(joint["name"])
2219
+ self.joint_type.append(joint["type"])
2220
+ self.joint_parent.append(body_remap[joint["parent"]])
2221
+ self.joint_child.append(body_remap[joint["child"]])
2222
+ self.joint_q_start.append(len(self.joint_q))
2223
+ self.joint_qd_start.append(len(self.joint_qd))
2224
+ self.joint_q.extend(joint["q"])
2225
+ self.joint_qd.extend(joint["qd"])
2226
+ self.joint_act.extend(joint["act"])
2227
+ self.joint_enabled.append(joint["enabled"])
2228
+ self.joint_linear_compliance.append(joint["linear_compliance"])
2229
+ self.joint_angular_compliance.append(joint["angular_compliance"])
2230
+ self.joint_X_p.append(list(joint["parent_xform"]))
2231
+ self.joint_X_c.append(list(joint["child_xform"]))
2232
+ self.joint_axis_dim.append(joint["axis_dim"])
2233
+ self.joint_axis_start.append(len(self.joint_axis))
2234
+ for axis in joint["axes"]:
2235
+ self.joint_axis.append(axis["axis"])
2236
+ self.joint_axis_mode.append(axis["axis_mode"])
2237
+ self.joint_target.append(axis["target"])
2238
+ self.joint_target_ke.append(axis["target_ke"])
2239
+ self.joint_target_kd.append(axis["target_kd"])
2240
+ self.joint_limit_lower.append(axis["limit_lower"])
2241
+ self.joint_limit_upper.append(axis["limit_upper"])
2242
+ self.joint_limit_ke.append(axis["limit_ke"])
2243
+ self.joint_limit_kd.append(axis["limit_kd"])
2244
+
1864
2245
  # muscles
1865
2246
  def add_muscle(
1866
2247
  self, bodies: List[int], positions: List[Vec3], f0: float, lm: float, lt: float, lmax: float, pen: float
1867
2248
  ) -> float:
1868
- """Adds a muscle-tendon activation unit
2249
+ """Adds a muscle-tendon activation unit.
1869
2250
 
1870
2251
  Args:
1871
2252
  bodies: A list of body indices for each waypoint
@@ -1878,6 +2259,8 @@ class ModelBuilder:
1878
2259
  Returns:
1879
2260
  The index of the muscle in the model
1880
2261
 
2262
+ .. note:: The simulation support for muscles is in progress and not yet fully functional.
2263
+
1881
2264
  """
1882
2265
 
1883
2266
  n = len(bodies)
@@ -1913,7 +2296,7 @@ class ModelBuilder:
1913
2296
  """
1914
2297
  Adds a plane collision shape.
1915
2298
  If pos and rot are defined, the plane is assumed to have its normal as (0, 1, 0).
1916
- Otherwise, the plane equation is used.
2299
+ Otherwise, the plane equation defined through the `plane` argument is used.
1917
2300
 
1918
2301
  Args:
1919
2302
  plane: The plane equation in form a*x + b*y + c*z + d = 0
@@ -1930,6 +2313,9 @@ class ModelBuilder:
1930
2313
  thickness: The thickness of the plane (0 by default) for collision handling
1931
2314
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
1932
2315
 
2316
+ Returns:
2317
+ The index of the added shape
2318
+
1933
2319
  """
1934
2320
  if pos is None or rot is None:
1935
2321
  # compute position and rotation from plane equation
@@ -1944,7 +2330,7 @@ class ModelBuilder:
1944
2330
  angle = np.arcsin(np.linalg.norm(c))
1945
2331
  axis = c / np.linalg.norm(c)
1946
2332
  rot = wp.quat_from_axis_angle(axis, angle)
1947
- scale = (width, length, 0.0)
2333
+ scale = wp.vec3(width, length, 0.0)
1948
2334
 
1949
2335
  return self._add_shape(
1950
2336
  body,
@@ -1982,7 +2368,7 @@ class ModelBuilder:
1982
2368
  """Adds a sphere collision shape to a body.
1983
2369
 
1984
2370
  Args:
1985
- body: The index of the parent body this shape belongs to
2371
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
1986
2372
  pos: The location of the shape with respect to the parent frame
1987
2373
  rot: The rotation of the shape with respect to the parent frame
1988
2374
  radius: The radius of the sphere
@@ -1996,14 +2382,17 @@ class ModelBuilder:
1996
2382
  thickness: Thickness to use for computing inertia of a hollow sphere, and for collision handling
1997
2383
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
1998
2384
 
2385
+ Returns:
2386
+ The index of the added shape
2387
+
1999
2388
  """
2000
2389
 
2001
2390
  return self._add_shape(
2002
2391
  body,
2003
- pos,
2004
- rot,
2392
+ wp.vec3(pos),
2393
+ wp.quat(rot),
2005
2394
  GEO_SPHERE,
2006
- (radius, 0.0, 0.0, 0.0),
2395
+ wp.vec3(radius, 0.0, 0.0),
2007
2396
  None,
2008
2397
  density,
2009
2398
  ke,
@@ -2037,7 +2426,7 @@ class ModelBuilder:
2037
2426
  """Adds a box collision shape to a body.
2038
2427
 
2039
2428
  Args:
2040
- body: The index of the parent body this shape belongs to
2429
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
2041
2430
  pos: The location of the shape with respect to the parent frame
2042
2431
  rot: The rotation of the shape with respect to the parent frame
2043
2432
  hx: The half-extent along the x-axis
@@ -2053,14 +2442,17 @@ class ModelBuilder:
2053
2442
  thickness: Thickness to use for computing inertia of a hollow box, and for collision handling
2054
2443
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
2055
2444
 
2445
+ Returns:
2446
+ The index of the added shape
2447
+
2056
2448
  """
2057
2449
 
2058
2450
  return self._add_shape(
2059
2451
  body,
2060
- pos,
2061
- rot,
2452
+ wp.vec3(pos),
2453
+ wp.quat(rot),
2062
2454
  GEO_BOX,
2063
- (hx, hy, hz, 0.0),
2455
+ wp.vec3(hx, hy, hz),
2064
2456
  None,
2065
2457
  density,
2066
2458
  ke,
@@ -2094,7 +2486,7 @@ class ModelBuilder:
2094
2486
  """Adds a capsule collision shape to a body.
2095
2487
 
2096
2488
  Args:
2097
- body: The index of the parent body this shape belongs to
2489
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
2098
2490
  pos: The location of the shape with respect to the parent frame
2099
2491
  rot: The rotation of the shape with respect to the parent frame
2100
2492
  radius: The radius of the capsule
@@ -2110,21 +2502,24 @@ class ModelBuilder:
2110
2502
  thickness: Thickness to use for computing inertia of a hollow capsule, and for collision handling
2111
2503
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
2112
2504
 
2505
+ Returns:
2506
+ The index of the added shape
2507
+
2113
2508
  """
2114
2509
 
2115
- q = rot
2510
+ q = wp.quat(rot)
2116
2511
  sqh = math.sqrt(0.5)
2117
2512
  if up_axis == 0:
2118
- q = wp.mul(rot, wp.quat(0.0, 0.0, -sqh, sqh))
2513
+ q = wp.mul(q, wp.quat(0.0, 0.0, -sqh, sqh))
2119
2514
  elif up_axis == 2:
2120
- q = wp.mul(rot, wp.quat(sqh, 0.0, 0.0, sqh))
2515
+ q = wp.mul(q, wp.quat(sqh, 0.0, 0.0, sqh))
2121
2516
 
2122
2517
  return self._add_shape(
2123
2518
  body,
2124
- pos,
2125
- q,
2519
+ wp.vec3(pos),
2520
+ wp.quat(q),
2126
2521
  GEO_CAPSULE,
2127
- (radius, half_height, 0.0, 0.0),
2522
+ wp.vec3(radius, half_height, 0.0),
2128
2523
  None,
2129
2524
  density,
2130
2525
  ke,
@@ -2158,7 +2553,7 @@ class ModelBuilder:
2158
2553
  """Adds a cylinder collision shape to a body.
2159
2554
 
2160
2555
  Args:
2161
- body: The index of the parent body this shape belongs to
2556
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
2162
2557
  pos: The location of the shape with respect to the parent frame
2163
2558
  rot: The rotation of the shape with respect to the parent frame
2164
2559
  radius: The radius of the cylinder
@@ -2174,6 +2569,9 @@ class ModelBuilder:
2174
2569
  thickness: Thickness to use for computing inertia of a hollow cylinder, and for collision handling
2175
2570
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
2176
2571
 
2572
+ Returns:
2573
+ The index of the added shape
2574
+
2177
2575
  """
2178
2576
 
2179
2577
  q = rot
@@ -2185,10 +2583,10 @@ class ModelBuilder:
2185
2583
 
2186
2584
  return self._add_shape(
2187
2585
  body,
2188
- pos,
2189
- q,
2586
+ wp.vec3(pos),
2587
+ wp.quat(q),
2190
2588
  GEO_CYLINDER,
2191
- (radius, half_height, 0.0, 0.0),
2589
+ wp.vec3(radius, half_height, 0.0),
2192
2590
  None,
2193
2591
  density,
2194
2592
  ke,
@@ -2222,7 +2620,7 @@ class ModelBuilder:
2222
2620
  """Adds a cone collision shape to a body.
2223
2621
 
2224
2622
  Args:
2225
- body: The index of the parent body this shape belongs to
2623
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
2226
2624
  pos: The location of the shape with respect to the parent frame
2227
2625
  rot: The rotation of the shape with respect to the parent frame
2228
2626
  radius: The radius of the cone
@@ -2238,6 +2636,9 @@ class ModelBuilder:
2238
2636
  thickness: Thickness to use for computing inertia of a hollow cone, and for collision handling
2239
2637
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
2240
2638
 
2639
+ Returns:
2640
+ The index of the added shape
2641
+
2241
2642
  """
2242
2643
 
2243
2644
  q = rot
@@ -2249,10 +2650,10 @@ class ModelBuilder:
2249
2650
 
2250
2651
  return self._add_shape(
2251
2652
  body,
2252
- pos,
2253
- q,
2653
+ wp.vec3(pos),
2654
+ wp.quat(q),
2254
2655
  GEO_CONE,
2255
- (radius, half_height, 0.0, 0.0),
2656
+ wp.vec3(radius, half_height, 0.0),
2256
2657
  None,
2257
2658
  density,
2258
2659
  ke,
@@ -2268,10 +2669,10 @@ class ModelBuilder:
2268
2669
  def add_shape_mesh(
2269
2670
  self,
2270
2671
  body: int,
2271
- pos: Vec3 = (0.0, 0.0, 0.0),
2272
- rot: Quat = (0.0, 0.0, 0.0, 1.0),
2672
+ pos: Vec3 = wp.vec3(0.0, 0.0, 0.0),
2673
+ rot: Quat = wp.quat(0.0, 0.0, 0.0, 1.0),
2273
2674
  mesh: Mesh = None,
2274
- scale: Vec3 = (1.0, 1.0, 1.0),
2675
+ scale: Vec3 = wp.vec3(1.0, 1.0, 1.0),
2275
2676
  density: float = default_shape_density,
2276
2677
  ke: float = default_shape_ke,
2277
2678
  kd: float = default_shape_kd,
@@ -2285,7 +2686,7 @@ class ModelBuilder:
2285
2686
  """Adds a triangle mesh collision shape to a body.
2286
2687
 
2287
2688
  Args:
2288
- body: The index of the parent body this shape belongs to
2689
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
2289
2690
  pos: The location of the shape with respect to the parent frame
2290
2691
  rot: The rotation of the shape with respect to the parent frame
2291
2692
  mesh: The mesh object
@@ -2300,6 +2701,9 @@ class ModelBuilder:
2300
2701
  thickness: Thickness to use for computing inertia of a hollow mesh, and for collision handling
2301
2702
  has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
2302
2703
 
2704
+ Returns:
2705
+ The index of the added shape
2706
+
2303
2707
  """
2304
2708
 
2305
2709
  return self._add_shape(
@@ -2307,7 +2711,7 @@ class ModelBuilder:
2307
2711
  pos,
2308
2712
  rot,
2309
2713
  GEO_MESH,
2310
- (scale[0], scale[1], scale[2], 0.0),
2714
+ wp.vec3(scale[0], scale[1], scale[2]),
2311
2715
  mesh,
2312
2716
  density,
2313
2717
  ke,
@@ -2320,6 +2724,61 @@ class ModelBuilder:
2320
2724
  has_ground_collision=has_ground_collision,
2321
2725
  )
2322
2726
 
2727
+ def add_shape_sdf(
2728
+ self,
2729
+ body: int,
2730
+ pos: Vec3 = (0.0, 0.0, 0.0),
2731
+ rot: Quat = (0.0, 0.0, 0.0, 1.0),
2732
+ sdf: SDF = None,
2733
+ scale: Vec3 = (1.0, 1.0, 1.0),
2734
+ density: float = default_shape_density,
2735
+ ke: float = default_shape_ke,
2736
+ kd: float = default_shape_kd,
2737
+ kf: float = default_shape_kf,
2738
+ mu: float = default_shape_mu,
2739
+ restitution: float = default_shape_restitution,
2740
+ is_solid: bool = True,
2741
+ thickness: float = default_geo_thickness,
2742
+ ):
2743
+ """Adds SDF collision shape to a body.
2744
+
2745
+ Args:
2746
+ body: The index of the parent body this shape belongs to (use -1 for static shapes)
2747
+ pos: The location of the shape with respect to the parent frame
2748
+ rot: The rotation of the shape with respect to the parent frame
2749
+ sdf: The sdf object
2750
+ scale: Scale to use for the collider
2751
+ density: The density of the shape
2752
+ ke: The contact elastic stiffness
2753
+ kd: The contact damping stiffness
2754
+ kf: The contact friction stiffness
2755
+ mu: The coefficient of friction
2756
+ restitution: The coefficient of restitution
2757
+ is_solid: If True, the mesh is solid, otherwise it is a hollow surface with the given wall thickness
2758
+ thickness: Thickness to use for computing inertia of a hollow mesh, and for collision handling
2759
+ has_ground_collision: If True, the mesh will collide with the ground plane if `Model.ground` is True
2760
+
2761
+ Returns:
2762
+ The index of the added shape
2763
+
2764
+ """
2765
+ return self._add_shape(
2766
+ body,
2767
+ wp.vec3(pos),
2768
+ wp.quat(rot),
2769
+ GEO_SDF,
2770
+ wp.vec3(scale[0], scale[1], scale[2]),
2771
+ sdf,
2772
+ density,
2773
+ ke,
2774
+ kd,
2775
+ kf,
2776
+ mu,
2777
+ restitution,
2778
+ thickness,
2779
+ is_solid,
2780
+ )
2781
+
2323
2782
  def _shape_radius(self, type, scale, src):
2324
2783
  """
2325
2784
  Calculates the radius of a sphere that encloses the shape, used for broadphase collision detection.
@@ -2399,7 +2858,7 @@ class ModelBuilder:
2399
2858
 
2400
2859
  (m, c, I) = compute_shape_mass(type, scale, src, density, is_solid, thickness)
2401
2860
 
2402
- self._update_body_mass(body, m, I, np.array(pos) + c, np.array(rot))
2861
+ self._update_body_mass(body, m, I, pos + c, rot)
2403
2862
  return shape
2404
2863
 
2405
2864
  # particles
@@ -2494,9 +2953,9 @@ class ModelBuilder:
2494
2953
 
2495
2954
  """
2496
2955
  # compute basis for 2D rest pose
2497
- p = np.array(self.particle_q[i])
2498
- q = np.array(self.particle_q[j])
2499
- r = np.array(self.particle_q[k])
2956
+ p = self.particle_q[i]
2957
+ q = self.particle_q[j]
2958
+ r = self.particle_q[k]
2500
2959
 
2501
2960
  qp = q - p
2502
2961
  rp = r - p
@@ -2634,7 +3093,7 @@ class ModelBuilder:
2634
3093
  The volume of the tetrahedron
2635
3094
 
2636
3095
  Note:
2637
- The tetrahedron is created with a rest-pose based on the particle's initial configruation
3096
+ The tetrahedron is created with a rest-pose based on the particle's initial configuration
2638
3097
 
2639
3098
  """
2640
3099
  # compute basis for 2D rest pose
@@ -2693,13 +3152,13 @@ class ModelBuilder:
2693
3152
  """
2694
3153
  # compute rest angle
2695
3154
  if rest is None:
2696
- x1 = np.array(self.particle_q[i])
2697
- x2 = np.array(self.particle_q[j])
2698
- x3 = np.array(self.particle_q[k])
2699
- x4 = np.array(self.particle_q[l])
3155
+ x1 = self.particle_q[i]
3156
+ x2 = self.particle_q[j]
3157
+ x3 = self.particle_q[k]
3158
+ x4 = self.particle_q[l]
2700
3159
 
2701
- n1 = wp.normalize(np.cross(x3 - x1, x4 - x1))
2702
- n2 = wp.normalize(np.cross(x4 - x2, x3 - x2))
3160
+ n1 = wp.normalize(wp.cross(x3 - x1, x4 - x1))
3161
+ n2 = wp.normalize(wp.cross(x4 - x2, x3 - x2))
2703
3162
  e = wp.normalize(x4 - x3)
2704
3163
 
2705
3164
  d = np.clip(np.dot(n2, n1), -1.0, 1.0)
@@ -2805,6 +3264,9 @@ class ModelBuilder:
2805
3264
  tri_lift: float = default_tri_lift,
2806
3265
  edge_ke: float = default_edge_ke,
2807
3266
  edge_kd: float = default_edge_kd,
3267
+ add_springs: bool = False,
3268
+ spring_ke: float = default_spring_ke,
3269
+ spring_kd: float = default_spring_kd,
2808
3270
  ):
2809
3271
  """Helper to create a regular planar cloth grid
2810
3272
 
@@ -2836,8 +3298,8 @@ class ModelBuilder:
2836
3298
 
2837
3299
  for y in range(0, dim_y + 1):
2838
3300
  for x in range(0, dim_x + 1):
2839
- g = np.array((x * cell_x, y * cell_y, 0.0))
2840
- p = np.array(wp.quat_rotate(rot, g)) + pos
3301
+ g = wp.vec3(x * cell_x, y * cell_y, 0.0)
3302
+ p = wp.quat_rotate(rot, g) + pos
2841
3303
  m = mass
2842
3304
 
2843
3305
  if x == 0 and fix_left:
@@ -2884,13 +3346,14 @@ class ModelBuilder:
2884
3346
  self.add_triangle(*tri1, tri_ke, tri_ka, tri_kd, tri_drag, tri_lift)
2885
3347
  self.add_triangle(*tri2, tri_ke, tri_ka, tri_kd, tri_drag, tri_lift)
2886
3348
 
2887
- end_vertex = len(self.particle_q)
2888
3349
  end_tri = len(self.tri_indices)
2889
3350
 
2890
3351
  # bending constraints, could create these explicitly for a grid but this
2891
3352
  # is a good test of the adjacency structure
2892
3353
  adj = wp.utils.MeshAdjacency(self.tri_indices[start_tri:end_tri], end_tri - start_tri)
2893
3354
 
3355
+ spring_indices = set()
3356
+
2894
3357
  for k, e in adj.edges.items():
2895
3358
  # skip open edges
2896
3359
  if e.f0 == -1 or e.f1 == -1:
@@ -2900,6 +3363,19 @@ class ModelBuilder:
2900
3363
  e.o0, e.o1, e.v0, e.v1, edge_ke=edge_ke, edge_kd=edge_kd
2901
3364
  ) # opposite 0, opposite 1, vertex 0, vertex 1
2902
3365
 
3366
+ spring_indices.add((min(e.o0, e.o1), max(e.o0, e.o1)))
3367
+ spring_indices.add((min(e.o0, e.v0), max(e.o0, e.v0)))
3368
+ spring_indices.add((min(e.o0, e.v1), max(e.o0, e.v1)))
3369
+
3370
+ spring_indices.add((min(e.o1, e.v0), max(e.o1, e.v0)))
3371
+ spring_indices.add((min(e.o1, e.v1), max(e.o1, e.v1)))
3372
+
3373
+ spring_indices.add((min(e.v0, e.v1), max(e.v0, e.v1)))
3374
+
3375
+ if add_springs:
3376
+ for i, j in spring_indices:
3377
+ self.add_spring(i, j, spring_ke, spring_kd, control=0.0)
3378
+
2903
3379
  def add_cloth_mesh(
2904
3380
  self,
2905
3381
  pos: Vec3,
@@ -2918,6 +3394,9 @@ class ModelBuilder:
2918
3394
  tri_lift: float = default_tri_lift,
2919
3395
  edge_ke: float = default_edge_ke,
2920
3396
  edge_kd: float = default_edge_kd,
3397
+ add_springs: bool = False,
3398
+ spring_ke: float = default_spring_ke,
3399
+ spring_kd: float = default_spring_kd,
2921
3400
  ):
2922
3401
  """Helper to create a cloth model from a regular triangle mesh
2923
3402
 
@@ -2945,7 +3424,7 @@ class ModelBuilder:
2945
3424
 
2946
3425
  # particles
2947
3426
  for v in vertices:
2948
- p = np.array(wp.quat_rotate(rot, v * scale)) + pos
3427
+ p = wp.quat_rotate(rot, v * scale) + pos
2949
3428
 
2950
3429
  self.add_particle(p, vel, 0.0)
2951
3430
 
@@ -2974,7 +3453,10 @@ class ModelBuilder:
2974
3453
 
2975
3454
  adj = wp.utils.MeshAdjacency(self.tri_indices[start_tri:end_tri], end_tri - start_tri)
2976
3455
 
2977
- edgeinds = np.array([[e.o0, e.o1, e.v0, e.v1] for k, e in adj.edges.items()])
3456
+ edgeinds = np.fromiter(
3457
+ (x for e in adj.edges.values() if e.f0 != -1 and e.f1 != -1 for x in (e.o0, e.o1, e.v0, e.v1)),
3458
+ int,
3459
+ ).reshape(-1, 4)
2978
3460
  self.add_edges(
2979
3461
  edgeinds[:, 0],
2980
3462
  edgeinds[:, 1],
@@ -2984,6 +3466,21 @@ class ModelBuilder:
2984
3466
  edge_kd=[edge_kd] * len(edgeinds),
2985
3467
  )
2986
3468
 
3469
+ if add_springs:
3470
+ spring_indices = set()
3471
+ for i, j, k, l in edgeinds:
3472
+ spring_indices.add((min(i, j), max(i, j)))
3473
+ spring_indices.add((min(i, k), max(i, k)))
3474
+ spring_indices.add((min(i, l), max(i, l)))
3475
+
3476
+ spring_indices.add((min(j, k), max(j, k)))
3477
+ spring_indices.add((min(j, l), max(j, l)))
3478
+
3479
+ spring_indices.add((min(k, l), max(k, l)))
3480
+
3481
+ for i, j in spring_indices:
3482
+ self.add_spring(i, j, spring_ke, spring_kd, control=0.0)
3483
+
2987
3484
  def add_particle_grid(
2988
3485
  self,
2989
3486
  pos: Vec3,
@@ -3000,13 +3497,14 @@ class ModelBuilder:
3000
3497
  radius_mean: float = default_particle_radius,
3001
3498
  radius_std: float = 0.0,
3002
3499
  ):
3500
+ rng = np.random.default_rng()
3003
3501
  for z in range(dim_z):
3004
3502
  for y in range(dim_y):
3005
3503
  for x in range(dim_x):
3006
- v = np.array((x * cell_x, y * cell_y, z * cell_z))
3504
+ v = wp.vec3(x * cell_x, y * cell_y, z * cell_z)
3007
3505
  m = mass
3008
3506
 
3009
- p = np.array(wp.quat_rotate(rot, v)) + pos + np.random.rand(3) * jitter
3507
+ p = wp.quat_rotate(rot, v) + pos + wp.vec3(rng.random(3) * jitter)
3010
3508
 
3011
3509
  if radius_std > 0.0:
3012
3510
  r = radius_mean + np.random.randn() * radius_std
@@ -3072,7 +3570,7 @@ class ModelBuilder:
3072
3570
  for z in range(dim_z + 1):
3073
3571
  for y in range(dim_y + 1):
3074
3572
  for x in range(dim_x + 1):
3075
- v = np.array((x * cell_x, y * cell_y, z * cell_z))
3573
+ v = wp.vec3(x * cell_x, y * cell_y, z * cell_z)
3076
3574
  m = mass
3077
3575
 
3078
3576
  if fix_left and x == 0:
@@ -3087,7 +3585,7 @@ class ModelBuilder:
3087
3585
  if fix_bottom and y == 0:
3088
3586
  m = 0.0
3089
3587
 
3090
- p = np.array(wp.quat_rotate(rot, v)) + pos
3588
+ p = wp.quat_rotate(rot, v) + pos
3091
3589
 
3092
3590
  self.add_particle(p, vel, m)
3093
3591
 
@@ -3177,7 +3675,6 @@ class ModelBuilder:
3177
3675
  num_tets = int(len(indices) / 4)
3178
3676
 
3179
3677
  start_vertex = len(self.particle_q)
3180
- start_tri = len(self.tri_indices)
3181
3678
 
3182
3679
  # dict of open faces
3183
3680
  faces = {}
@@ -3255,8 +3752,8 @@ class ModelBuilder:
3255
3752
  else:
3256
3753
  self.body_inv_mass[i] = 0.0
3257
3754
 
3258
- if new_inertia.any():
3259
- self.body_inv_inertia[i] = np.linalg.inv(new_inertia)
3755
+ if any(x for x in new_inertia):
3756
+ self.body_inv_inertia[i] = wp.inverse(new_inertia)
3260
3757
  else:
3261
3758
  self.body_inv_inertia[i] = new_inertia
3262
3759
 
@@ -3338,6 +3835,7 @@ class ModelBuilder:
3338
3835
  m._particle_radius = wp.array(self.particle_radius, dtype=wp.float32, requires_grad=requires_grad)
3339
3836
  m.particle_flags = wp.array([flag_to_int(f) for f in self.particle_flags], dtype=wp.uint32)
3340
3837
  m.particle_max_radius = np.max(self.particle_radius) if len(self.particle_radius) > 0 else 0.0
3838
+ m.particle_max_velocity = self.particle_max_velocity
3341
3839
 
3342
3840
  # hash-grid for particle interactions
3343
3841
  m.particle_grid = wp.HashGrid(128, 128, 128)
@@ -3393,6 +3891,9 @@ class ModelBuilder:
3393
3891
  m.spring_stiffness = wp.array(self.spring_stiffness, dtype=wp.float32, requires_grad=requires_grad)
3394
3892
  m.spring_damping = wp.array(self.spring_damping, dtype=wp.float32, requires_grad=requires_grad)
3395
3893
  m.spring_control = wp.array(self.spring_control, dtype=wp.float32, requires_grad=requires_grad)
3894
+ m.spring_constraint_lambdas = wp.array(
3895
+ shape=len(self.spring_rest_length), dtype=wp.float32, requires_grad=requires_grad
3896
+ )
3396
3897
 
3397
3898
  # ---------------------
3398
3899
  # triangles
@@ -3410,6 +3911,9 @@ class ModelBuilder:
3410
3911
  m.edge_bending_properties = wp.array(
3411
3912
  self.edge_bending_properties, dtype=wp.float32, requires_grad=requires_grad
3412
3913
  )
3914
+ m.edge_constraint_lambdas = wp.array(
3915
+ shape=len(self.edge_rest_angle), dtype=wp.float32, requires_grad=requires_grad
3916
+ )
3413
3917
 
3414
3918
  # ---------------------
3415
3919
  # tetrahedra