@woosh/meep-engine 3.43.0 → 3.45.0

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.
Files changed (466) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/editor/prototypeEditorShell.d.ts.map +1 -1
  3. package/editor/prototypeEditorShell.js +4 -2
  4. package/package.json +1 -1
  5. package/src/core/collection/array/array_sort_insertion_ascending.d.ts +19 -0
  6. package/src/core/collection/array/array_sort_insertion_ascending.d.ts.map +1 -0
  7. package/src/core/collection/array/array_sort_insertion_ascending.js +31 -0
  8. package/src/core/geom/3d/aabb/aabb3_array_round_outward_f32.d.ts +27 -0
  9. package/src/core/geom/3d/aabb/aabb3_array_round_outward_f32.d.ts.map +1 -0
  10. package/src/core/geom/3d/aabb/aabb3_array_round_outward_f32.js +39 -0
  11. package/src/core/geom/3d/aabb/aabb3_from_v3_array.d.ts.map +1 -1
  12. package/src/core/geom/3d/aabb/aabb3_from_v3_array.js +4 -1
  13. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts +2 -2
  14. package/src/core/geom/3d/aabb/aabb3_transform_oriented.d.ts.map +1 -1
  15. package/src/core/geom/3d/aabb/aabb3_transform_oriented.js +1 -1
  16. package/src/core/geom/3d/pose/pose3_apply_inverse_point_array.d.ts +21 -0
  17. package/src/core/geom/3d/pose/pose3_apply_inverse_point_array.d.ts.map +1 -0
  18. package/src/core/geom/3d/pose/pose3_apply_inverse_point_array.js +36 -0
  19. package/src/core/geom/3d/pose/pose3_apply_point_array.d.ts +25 -0
  20. package/src/core/geom/3d/pose/pose3_apply_point_array.d.ts.map +1 -0
  21. package/src/core/geom/3d/pose/pose3_apply_point_array.js +40 -0
  22. package/src/core/geom/3d/pose/pose3_compose_array.d.ts +28 -0
  23. package/src/core/geom/3d/pose/pose3_compose_array.d.ts.map +1 -0
  24. package/src/core/geom/3d/pose/pose3_compose_array.js +41 -0
  25. package/src/core/geom/3d/pose/pose3_to_local_array.d.ts +29 -0
  26. package/src/core/geom/3d/pose/pose3_to_local_array.d.ts.map +1 -0
  27. package/src/core/geom/3d/pose/pose3_to_local_array.js +42 -0
  28. package/src/core/geom/3d/quaternion/quat3_clamp_angle_array.d.ts +28 -0
  29. package/src/core/geom/3d/quaternion/quat3_clamp_angle_array.d.ts.map +1 -0
  30. package/src/core/geom/3d/quaternion/quat3_clamp_angle_array.js +66 -0
  31. package/src/core/geom/3d/quaternion/quat3_multiply_conjugate_left_array.d.ts +22 -0
  32. package/src/core/geom/3d/quaternion/quat3_multiply_conjugate_left_array.d.ts.map +1 -0
  33. package/src/core/geom/3d/quaternion/quat3_multiply_conjugate_left_array.js +30 -0
  34. package/src/core/geom/3d/quaternion/quat3_multiply_conjugate_right_array.d.ts +23 -0
  35. package/src/core/geom/3d/quaternion/quat3_multiply_conjugate_right_array.d.ts.map +1 -0
  36. package/src/core/geom/3d/quaternion/quat3_multiply_conjugate_right_array.js +31 -0
  37. package/src/core/geom/3d/triangle/tri3_compute_normal_area_weighted_array.d.ts +26 -0
  38. package/src/core/geom/3d/triangle/tri3_compute_normal_area_weighted_array.d.ts.map +1 -0
  39. package/src/core/geom/3d/triangle/tri3_compute_normal_area_weighted_array.js +46 -0
  40. package/src/core/geom/vec3/v3_distance_array.d.ts +11 -0
  41. package/src/core/geom/vec3/v3_distance_array.d.ts.map +1 -0
  42. package/src/core/geom/vec3/v3_distance_array.js +19 -0
  43. package/src/core/geom/vec3/v3_normalize_array_checked.d.ts +23 -0
  44. package/src/core/geom/vec3/v3_normalize_array_checked.d.ts.map +1 -0
  45. package/src/core/geom/vec3/v3_normalize_array_checked.js +49 -0
  46. package/src/core/geom/vec3/v3_reject_array.d.ts +15 -0
  47. package/src/core/geom/vec3/v3_reject_array.d.ts.map +1 -0
  48. package/src/core/geom/vec3/v3_reject_array.js +32 -0
  49. package/src/core/graph/csr/csr_build_incidence.d.ts +26 -0
  50. package/src/core/graph/csr/csr_build_incidence.d.ts.map +1 -0
  51. package/src/core/graph/csr/csr_build_incidence.js +71 -0
  52. package/src/core/graph/csr/csr_graph_color_greedy.d.ts +25 -0
  53. package/src/core/graph/csr/csr_graph_color_greedy.d.ts.map +1 -0
  54. package/src/core/graph/csr/csr_graph_color_greedy.js +83 -0
  55. package/src/core/graph/csr/csr_graph_validate_coloring.d.ts +15 -0
  56. package/src/core/graph/csr/csr_graph_validate_coloring.d.ts.map +1 -0
  57. package/src/core/graph/csr/csr_graph_validate_coloring.js +33 -0
  58. package/src/core/math/linalg/matrix_symmetric_3x3_solve_ldlt.d.ts +26 -0
  59. package/src/core/math/linalg/matrix_symmetric_3x3_solve_ldlt.d.ts.map +1 -0
  60. package/src/core/math/linalg/matrix_symmetric_3x3_solve_ldlt.js +71 -0
  61. package/src/core/math/max2_exact.d.ts +15 -0
  62. package/src/core/math/max2_exact.d.ts.map +1 -0
  63. package/src/core/math/max2_exact.js +30 -0
  64. package/src/core/math/min2_exact.d.ts +20 -0
  65. package/src/core/math/min2_exact.d.ts.map +1 -0
  66. package/src/core/math/min2_exact.js +35 -0
  67. package/src/engine/Clock.d.ts +15 -0
  68. package/src/engine/Clock.d.ts.map +1 -1
  69. package/src/engine/Clock.js +29 -5
  70. package/src/engine/Engine.d.ts.map +1 -1
  71. package/src/engine/Engine.js +11 -19
  72. package/src/engine/ecs/ik/IKGoalSystem.d.ts.map +1 -1
  73. package/src/engine/ecs/ik/IKGoalSystem.js +3 -19
  74. package/src/engine/ecs/parent/parent_entity_model_of.d.ts +17 -0
  75. package/src/engine/ecs/parent/parent_entity_model_of.d.ts.map +1 -0
  76. package/src/engine/ecs/parent/parent_entity_model_of.js +26 -0
  77. package/src/engine/ecs/transform/t64_read_pose_array.d.ts +15 -0
  78. package/src/engine/ecs/transform/t64_read_pose_array.d.ts.map +1 -0
  79. package/src/engine/ecs/transform/t64_read_pose_array.js +24 -0
  80. package/src/engine/graphics/ecs/mesh-v2/MeshSkin.d.ts +57 -0
  81. package/src/engine/graphics/ecs/mesh-v2/MeshSkin.d.ts.map +1 -0
  82. package/src/engine/graphics/ecs/mesh-v2/MeshSkin.js +84 -0
  83. package/src/engine/graphics/geometry/geometry_construct_triangle_normal_area_weighted.d.ts +2 -0
  84. package/src/engine/graphics/geometry/geometry_construct_triangle_normal_area_weighted.d.ts.map +1 -1
  85. package/src/engine/graphics/geometry/geometry_construct_triangle_normal_area_weighted.js +10 -21
  86. package/src/engine/graphics3/AnimationGraphSystem.d.ts +28 -13
  87. package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -1
  88. package/src/engine/graphics3/AnimationGraphSystem.js +107 -64
  89. package/src/engine/graphics3/AnimationSystem.d.ts +29 -15
  90. package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -1
  91. package/src/engine/graphics3/AnimationSystem.js +112 -61
  92. package/src/engine/graphics3/MeshSkinSystem.d.ts +56 -0
  93. package/src/engine/graphics3/MeshSkinSystem.d.ts.map +1 -0
  94. package/src/engine/graphics3/MeshSkinSystem.js +317 -0
  95. package/src/engine/graphics3/MeshSystem.d.ts +15 -35
  96. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
  97. package/src/engine/graphics3/MeshSystem.js +21 -298
  98. package/src/engine/graphics3/RowChains.d.ts +76 -0
  99. package/src/engine/graphics3/RowChains.d.ts.map +1 -0
  100. package/src/engine/graphics3/RowChains.js +347 -0
  101. package/src/engine/graphics3/ShadedGeometrySystem.d.ts +6 -0
  102. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
  103. package/src/engine/graphics3/ShadedGeometrySystem.js +329 -309
  104. package/src/engine/graphics3/animation/AnimationClips.d.ts +56 -0
  105. package/src/engine/graphics3/animation/AnimationClips.d.ts.map +1 -0
  106. package/src/engine/graphics3/animation/AnimationClips.js +79 -0
  107. package/src/engine/graphics3/animation/ClipListPlayer.d.ts +13 -14
  108. package/src/engine/graphics3/animation/ClipListPlayer.d.ts.map +1 -1
  109. package/src/engine/graphics3/animation/ClipListPlayer.js +262 -263
  110. package/src/engine/graphics3/animation/write_clip_playback.d.ts +3 -3
  111. package/src/engine/graphics3/animation/write_clip_playback.js +4 -4
  112. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts +5 -0
  113. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts.map +1 -1
  114. package/src/engine/graphics3/pose/query_entity_node_world_pose.js +80 -75
  115. package/src/engine/graphics3/prefab/PrefabInstance.d.ts +4 -24
  116. package/src/engine/graphics3/prefab/PrefabInstance.d.ts.map +1 -1
  117. package/src/engine/graphics3/prefab/PrefabInstance.js +60 -80
  118. package/src/engine/graphics3/prefab/prefab_instantiate.d.ts +16 -3
  119. package/src/engine/graphics3/prefab/prefab_instantiate.d.ts.map +1 -1
  120. package/src/engine/graphics3/prefab/prefab_instantiate.js +49 -12
  121. package/src/engine/graphics3/prefab/prefab_place.d.ts +31 -17
  122. package/src/engine/graphics3/prefab/prefab_place.d.ts.map +1 -1
  123. package/src/engine/graphics3/prefab/prefab_place.js +175 -130
  124. package/src/engine/graphics3/register_clips.d.ts +93 -0
  125. package/src/engine/graphics3/register_clips.d.ts.map +1 -0
  126. package/src/engine/graphics3/register_clips.js +166 -0
  127. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts +9 -0
  128. package/src/engine/physics/broadphase/compute_fat_world_aabb.d.ts.map +1 -1
  129. package/src/engine/physics/broadphase/compute_fat_world_aabb.js +3 -1
  130. package/src/engine/physics/cloth/MEASUREMENTS.md +795 -0
  131. package/src/engine/physics/cloth/PLAN.md +349 -17
  132. package/src/engine/physics/cloth/build/ClothProxy.d.ts +113 -61
  133. package/src/engine/physics/cloth/build/ClothProxy.d.ts.map +1 -1
  134. package/src/engine/physics/cloth/build/ClothProxy.js +142 -77
  135. package/src/engine/physics/cloth/build/cloth_bind_in_frame.d.ts +44 -0
  136. package/src/engine/physics/cloth/build/cloth_bind_in_frame.d.ts.map +1 -0
  137. package/src/engine/physics/cloth/build/cloth_bind_in_frame.js +107 -0
  138. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts +43 -30
  139. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -1
  140. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +371 -284
  141. package/src/engine/physics/cloth/build/cloth_proxy_from_mesh.d.ts +62 -0
  142. package/src/engine/physics/cloth/build/cloth_proxy_from_mesh.d.ts.map +1 -0
  143. package/src/engine/physics/cloth/build/cloth_proxy_from_mesh.js +275 -0
  144. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +7 -0
  145. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
  146. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +85 -67
  147. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts.map +1 -1
  148. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +19 -101
  149. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts +2 -5
  150. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts.map +1 -1
  151. package/src/engine/physics/cloth/collider/cloth_collider_pack.js +7 -15
  152. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +1 -1
  153. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -1
  154. package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +17 -62
  155. package/src/engine/physics/cloth/ecs/Cloth.d.ts +17 -9
  156. package/src/engine/physics/cloth/ecs/Cloth.d.ts.map +1 -1
  157. package/src/engine/physics/cloth/ecs/Cloth.js +220 -212
  158. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts +0 -1
  159. package/src/engine/physics/cloth/ecs/ClothColliderFlags.js +6 -19
  160. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +1 -2
  161. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -1
  162. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +3 -12
  163. package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.js +6 -0
  164. package/src/engine/physics/cloth/ecs/ClothHold.d.ts +91 -0
  165. package/src/engine/physics/cloth/ecs/ClothHold.d.ts.map +1 -0
  166. package/src/engine/physics/cloth/ecs/ClothHold.js +103 -0
  167. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +254 -48
  168. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  169. package/src/engine/physics/cloth/ecs/ClothInstance.js +277 -47
  170. package/src/engine/physics/cloth/ecs/ClothPin.d.ts +73 -0
  171. package/src/engine/physics/cloth/ecs/ClothPin.d.ts.map +1 -0
  172. package/src/engine/physics/cloth/ecs/ClothPin.js +92 -0
  173. package/src/engine/physics/cloth/ecs/ClothPinSerializationAdapter.d.ts +36 -0
  174. package/src/engine/physics/cloth/ecs/ClothPinSerializationAdapter.d.ts.map +1 -0
  175. package/src/engine/physics/cloth/ecs/ClothPinSerializationAdapter.js +61 -0
  176. package/src/engine/physics/cloth/ecs/ClothRig.d.ts +23 -17
  177. package/src/engine/physics/cloth/ecs/ClothRig.d.ts.map +1 -1
  178. package/src/engine/physics/cloth/ecs/ClothRig.js +29 -27
  179. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +12 -14
  180. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  181. package/src/engine/physics/cloth/ecs/ClothSystem.js +20 -15
  182. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +57 -36
  183. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  184. package/src/engine/physics/cloth/ecs/ClothWorld.js +754 -207
  185. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +12 -13
  186. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -1
  187. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +20 -14
  188. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +63 -11
  189. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  190. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +297 -101
  191. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +9 -7
  192. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
  193. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +12 -10
  194. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +3 -3
  195. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  196. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +60 -120
  197. package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +37 -0
  198. package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts.map +1 -0
  199. package/src/engine/physics/cloth/ecs/cloth_geodesic.js +135 -0
  200. package/src/engine/physics/cloth/ecs/cloth_holds.d.ts +97 -0
  201. package/src/engine/physics/cloth/ecs/cloth_holds.d.ts.map +1 -0
  202. package/src/engine/physics/cloth/ecs/cloth_holds.js +583 -0
  203. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts +2 -2
  204. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -1
  205. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +168 -51
  206. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +17 -22
  207. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
  208. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +55 -117
  209. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +21 -6
  210. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
  211. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +51 -42
  212. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +5 -0
  213. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  214. package/src/engine/physics/cloth/ecs/cloth_write_back.js +62 -50
  215. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts.map +1 -1
  216. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.js +65 -58
  217. package/src/engine/physics/cloth/playground/cloth_playground_build.d.ts.map +1 -1
  218. package/src/engine/physics/cloth/playground/cloth_playground_build.js +2 -3
  219. package/src/engine/physics/cloth/playground/collide_main.js +3 -29
  220. package/src/engine/physics/cloth/playground/garment_build.d.ts.map +1 -1
  221. package/src/engine/physics/cloth/playground/garment_build.js +17 -5
  222. package/src/engine/physics/cloth/playground/garment_main.js +5 -20
  223. package/src/engine/physics/cloth/playground/main.js +6 -48
  224. package/src/engine/physics/cloth/playground/rig_build.d.ts.map +1 -1
  225. package/src/engine/physics/cloth/playground/rig_build.js +302 -296
  226. package/src/engine/physics/cloth/playground/rig_main.js +947 -955
  227. package/src/engine/physics/cloth/playground/wind_main.js +8 -7
  228. package/src/engine/physics/cloth/solver/ClothState.d.ts +45 -7
  229. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  230. package/src/engine/physics/cloth/solver/ClothState.js +79 -83
  231. package/src/engine/physics/cloth/solver/cloth_color_particles.d.ts +10 -8
  232. package/src/engine/physics/cloth/solver/cloth_color_particles.d.ts.map +1 -1
  233. package/src/engine/physics/cloth/solver/cloth_color_particles.js +30 -102
  234. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +6 -1
  235. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
  236. package/src/engine/physics/cloth/solver/cloth_contact_find.js +9 -3
  237. package/src/engine/physics/cloth/solver/cloth_hold_line.d.ts +114 -0
  238. package/src/engine/physics/cloth/solver/cloth_hold_line.d.ts.map +1 -0
  239. package/src/engine/physics/cloth/solver/cloth_hold_line.js +350 -0
  240. package/src/engine/physics/cloth/solver/cloth_kinetic_energy.d.ts +21 -0
  241. package/src/engine/physics/cloth/solver/cloth_kinetic_energy.d.ts.map +1 -0
  242. package/src/engine/physics/cloth/solver/cloth_kinetic_energy.js +45 -0
  243. package/src/engine/physics/cloth/solver/cloth_particle_coupling.d.ts +32 -0
  244. package/src/engine/physics/cloth/solver/cloth_particle_coupling.d.ts.map +1 -0
  245. package/src/engine/physics/cloth/solver/cloth_particle_coupling.js +133 -0
  246. package/src/engine/physics/cloth/solver/cloth_particle_of_source.d.ts +16 -0
  247. package/src/engine/physics/cloth/solver/cloth_particle_of_source.d.ts.map +1 -0
  248. package/src/engine/physics/cloth/solver/cloth_particle_of_source.js +19 -0
  249. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +6 -2
  250. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -1
  251. package/src/engine/physics/cloth/solver/cloth_self_find.js +19 -68
  252. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -1
  253. package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +30 -65
  254. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +24 -12
  255. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  256. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +24 -12
  257. package/src/engine/physics/cloth/solver/cloth_step.d.ts +16 -0
  258. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  259. package/src/engine/physics/cloth/solver/cloth_step.js +189 -48
  260. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +1 -0
  261. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -1
  262. package/src/engine/physics/cloth/solver/cloth_step_instance.js +12 -6
  263. package/src/engine/physics/cloth/solver/cloth_stretch_residual.d.ts +21 -0
  264. package/src/engine/physics/cloth/solver/cloth_stretch_residual.d.ts.map +1 -0
  265. package/src/engine/physics/cloth/solver/cloth_stretch_residual.js +54 -0
  266. package/src/engine/physics/cloth/solver/cloth_vertex_accumulator.d.ts +15 -0
  267. package/src/engine/physics/cloth/solver/cloth_vertex_accumulator.d.ts.map +1 -1
  268. package/src/engine/physics/cloth/solver/cloth_vertex_accumulator.js +28 -0
  269. package/src/engine/physics/cloth/solver/cloth_worst_tether_ratio.d.ts +30 -0
  270. package/src/engine/physics/cloth/solver/cloth_worst_tether_ratio.d.ts.map +1 -0
  271. package/src/engine/physics/cloth/solver/cloth_worst_tether_ratio.js +56 -0
  272. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -1
  273. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +3 -17
  274. package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.d.ts +1 -1
  275. package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.d.ts.map +1 -1
  276. package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.js +353 -346
  277. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts +7 -5
  278. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -1
  279. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +7 -5
  280. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts +2 -1
  281. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -1
  282. package/src/engine/physics/cloth/wind/ClothFluidWind.js +7 -25
  283. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +12 -10
  284. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -1
  285. package/src/engine/physics/cloth/wind/cloth_sample_air.js +60 -82
  286. package/src/engine/physics/ecs/ContactProbeSystem.d.ts.map +1 -1
  287. package/src/engine/physics/ecs/ContactProbeSystem.js +2 -18
  288. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  289. package/src/engine/physics/ecs/PhysicsSystem.js +9 -50
  290. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  291. package/src/engine/physics/fluid/ecs/fluid_sample_domain_bounds.d.ts +24 -0
  292. package/src/engine/physics/fluid/ecs/fluid_sample_domain_bounds.d.ts.map +1 -0
  293. package/src/engine/physics/fluid/ecs/fluid_sample_domain_bounds.js +42 -0
  294. package/src/engine/physics/fluid/ecs/fluid_sample_velocity_world_mps.d.ts +25 -0
  295. package/src/engine/physics/fluid/ecs/fluid_sample_velocity_world_mps.d.ts.map +1 -0
  296. package/src/engine/physics/fluid/ecs/fluid_sample_velocity_world_mps.js +40 -0
  297. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.d.ts.map +1 -1
  298. package/src/engine/physics/inverse_kinematics/frame_fabrik/frame_fabrik_solve.js +40 -90
  299. package/src/engine/physics/narrowphase/capsule_contacts.d.ts.map +1 -1
  300. package/src/engine/physics/narrowphase/capsule_contacts.js +3 -5
  301. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.d.ts.map +1 -1
  302. package/src/engine/physics/narrowphase/decomposition/mesh_enumerate_triangles.js +2 -14
  303. package/src/engine/physics/narrowphase/hull_sphere_contact.d.ts.map +1 -1
  304. package/src/engine/physics/narrowphase/hull_sphere_contact.js +2 -5
  305. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  306. package/src/engine/physics/narrowphase/narrowphase_step.js +13 -32
  307. package/src/engine/physics/narrowphase/sphere_box_contact.d.ts.map +1 -1
  308. package/src/engine/physics/narrowphase/sphere_box_contact.js +2 -5
  309. package/src/engine/simulation/Ticker.d.ts +13 -0
  310. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  311. package/src/engine/simulation/Ticker.js +43 -8
  312. package/src/format/texture/basis/transcode/etc1s_to_astc.d.ts.map +1 -1
  313. package/src/format/texture/basis/transcode/etc1s_to_astc.js +2 -1
  314. package/src/shade/RENDERER_CONTRACT.md +8 -2
  315. package/src/shade/playground/image_decode_bench/main.js +2 -1
  316. package/src/shade/playground/mesh_row_ceiling/main.js +2 -1
  317. package/src/shade/playground/skinned_blas_refit/diff_traced_against_rasterized.d.ts.map +1 -1
  318. package/src/shade/playground/skinned_blas_refit/diff_traced_against_rasterized.js +2 -1
  319. package/src/shade/playground/skinned_blas_refit/main.d.ts.map +1 -1
  320. package/src/shade/playground/skinned_blas_refit/main.js +11 -19
  321. package/src/shade/playground/skinned_blas_refit/place_character.d.ts +6 -3
  322. package/src/shade/playground/skinned_blas_refit/place_character.d.ts.map +1 -1
  323. package/src/shade/playground/skinned_blas_refit/place_character.js +90 -90
  324. package/src/shade/playground/skinned_mesh_soup/main.d.ts.map +1 -1
  325. package/src/shade/playground/skinned_mesh_soup/main.js +502 -509
  326. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.d.ts.map +1 -1
  327. package/src/shade/playground/skinned_mesh_soup/verify_clone_vertices.js +2 -1
  328. package/src/shade/playground/ssr_variance/statistics.d.ts.map +1 -1
  329. package/src/shade/playground/ssr_variance/statistics.js +2 -1
  330. package/src/shade/playground/texture_encode/README.md +129 -109
  331. package/src/shade/playground/texture_encode/main.d.ts.map +1 -1
  332. package/src/shade/playground/texture_encode/main.js +1431 -1430
  333. package/src/shade/playground/volumetrics_froxel/measure.d.ts.map +1 -1
  334. package/src/shade/playground/volumetrics_froxel/measure.js +2 -1
  335. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +45 -14
  336. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  337. package/src/shade/renderer/animation/GPUAnimationManager.js +316 -64
  338. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts +5 -0
  339. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  340. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +11 -1
  341. package/src/shade/renderer/buffer/table/gpu_database_check_links.d.ts.map +1 -1
  342. package/src/shade/renderer/buffer/table/gpu_database_check_links.js +2 -1
  343. package/src/shade/renderer/geometry/BoxGeometry.d.ts +1 -11
  344. package/src/shade/renderer/geometry/BoxGeometry.d.ts.map +1 -1
  345. package/src/shade/renderer/geometry/BoxGeometry.js +6 -13
  346. package/src/shade/renderer/geometry/CapsuleGeometry.d.ts +19 -0
  347. package/src/shade/renderer/geometry/CapsuleGeometry.d.ts.map +1 -0
  348. package/src/shade/renderer/geometry/CapsuleGeometry.js +24 -0
  349. package/src/shade/renderer/geometry/ConeGeometry.d.ts +20 -0
  350. package/src/shade/renderer/geometry/ConeGeometry.d.ts.map +1 -0
  351. package/src/shade/renderer/geometry/ConeGeometry.js +33 -0
  352. package/src/shade/renderer/geometry/CylinderGeometry.d.ts +21 -0
  353. package/src/shade/renderer/geometry/CylinderGeometry.d.ts.map +1 -0
  354. package/src/shade/renderer/geometry/CylinderGeometry.js +35 -0
  355. package/src/shade/renderer/geometry/PlaneGeometry.d.ts +17 -0
  356. package/src/shade/renderer/geometry/PlaneGeometry.d.ts.map +1 -0
  357. package/src/shade/renderer/geometry/PlaneGeometry.js +22 -0
  358. package/src/shade/renderer/geometry/TorusGeometry.d.ts +18 -0
  359. package/src/shade/renderer/geometry/TorusGeometry.d.ts.map +1 -0
  360. package/src/shade/renderer/geometry/TorusGeometry.js +23 -0
  361. package/src/shade/renderer/geometry/UVSphereGeometry.d.ts +21 -0
  362. package/src/shade/renderer/geometry/UVSphereGeometry.d.ts.map +1 -0
  363. package/src/shade/renderer/geometry/UVSphereGeometry.js +34 -0
  364. package/src/shade/renderer/geometry/primitives/make_capsule_geometry.d.ts +19 -0
  365. package/src/shade/renderer/geometry/primitives/make_capsule_geometry.d.ts.map +1 -0
  366. package/src/shade/renderer/geometry/primitives/make_capsule_geometry.js +205 -0
  367. package/src/shade/renderer/geometry/primitives/make_cone_geometry.d.ts +21 -0
  368. package/src/shade/renderer/geometry/primitives/make_cone_geometry.d.ts.map +1 -0
  369. package/src/shade/renderer/geometry/primitives/make_cone_geometry.js +36 -0
  370. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.d.ts +1 -1
  371. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.d.ts.map +1 -1
  372. package/src/shade/renderer/geometry/primitives/make_cylinder_geometry.js +44 -9
  373. package/src/shade/renderer/geometry/primitives/make_uv_sphere_geometry.d.ts +20 -0
  374. package/src/shade/renderer/geometry/primitives/make_uv_sphere_geometry.d.ts.map +1 -0
  375. package/src/shade/renderer/geometry/primitives/make_uv_sphere_geometry.js +190 -0
  376. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts.map +1 -1
  377. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +3 -2
  378. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts.map +1 -1
  379. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.js +2 -1
  380. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts.map +1 -1
  381. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.js +2 -1
  382. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts.map +1 -1
  383. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.js +2 -1
  384. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts.map +1 -1
  385. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.js +3 -2
  386. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -1
  387. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.js +2 -1
  388. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -1
  389. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.js +2 -1
  390. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  391. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +3 -2
  392. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -1
  393. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +7 -2
  394. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts +26 -8
  395. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts.map +1 -1
  396. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js +56 -39
  397. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_prefix_scan_csdldf.d.ts +7 -0
  398. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_prefix_scan_csdldf.d.ts.map +1 -1
  399. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/shader_prefix_scan_csdldf.js +46 -4
  400. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/test_prefix_sum.d.ts.map +1 -1
  401. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/test_prefix_sum.js +4 -2
  402. package/src/shade/renderer/gpu_primitive/sort/defs.d.ts +2 -2
  403. package/src/shade/renderer/gpu_primitive/sort/defs.js +79 -79
  404. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +1 -1
  405. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
  406. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +7 -3
  407. package/src/shade/renderer/particles/GPUParticleSystem.js +1 -1
  408. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +4 -3
  409. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
  410. package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +200 -194
  411. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  412. package/src/shade/renderer/particles/graph_particles.js +6 -2
  413. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.d.ts.map +1 -1
  414. package/src/shade/renderer/particles/optimizer/dag/particle_vector_extract.js +3 -2
  415. package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts.map +1 -1
  416. package/src/shade/renderer/particles/warmup/graph_particle_warmup.js +334 -329
  417. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  418. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +7 -2
  419. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
  420. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +16 -4
  421. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +2444 -2345
  422. package/src/shade/renderer/texture/encode/chunk_astc_encode_block_4x4_rgb.d.ts.map +1 -1
  423. package/src/shade/renderer/texture/encode/chunk_astc_encode_block_4x4_rgb.js +312 -310
  424. package/src/shade/renderer/texture/encode/chunk_bc1_encode_block.d.ts +3 -3
  425. package/src/shade/renderer/texture/encode/chunk_bc1_encode_block.d.ts.map +1 -1
  426. package/src/shade/renderer/texture/encode/chunk_bc1_encode_block.js +383 -375
  427. package/src/shade/renderer/texture/encode/chunk_bc6h_encode_block_mode11.d.ts.map +1 -1
  428. package/src/shade/renderer/texture/encode/chunk_bc6h_encode_block_mode11.js +349 -346
  429. package/src/shade/renderer/texture/encode/chunk_bc7_encode_block.d.ts +58 -0
  430. package/src/shade/renderer/texture/encode/chunk_bc7_encode_block.d.ts.map +1 -0
  431. package/src/shade/renderer/texture/encode/chunk_bc7_encode_block.js +536 -0
  432. package/src/shade/renderer/texture/encode/chunk_bc7_encode_block_mode6.d.ts +21 -7
  433. package/src/shade/renderer/texture/encode/chunk_bc7_encode_block_mode6.d.ts.map +1 -1
  434. package/src/shade/renderer/texture/encode/chunk_bc7_encode_block_mode6.js +291 -327
  435. package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode1.d.ts +25 -0
  436. package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode1.d.ts.map +1 -0
  437. package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode1.js +99 -0
  438. package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode6.d.ts +5 -3
  439. package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode6.d.ts.map +1 -1
  440. package/src/shade/renderer/texture/encode/chunk_bc7_pack_mode6.js +47 -71
  441. package/src/shade/renderer/texture/encode/chunk_bc7_partition_2.d.ts +10 -0
  442. package/src/shade/renderer/texture/encode/chunk_bc7_partition_2.d.ts.map +1 -0
  443. package/src/shade/renderer/texture/encode/chunk_bc7_partition_2.js +50 -0
  444. package/src/shade/renderer/texture/encode/shader_texture_encode_bc7.d.ts +4 -5
  445. package/src/shade/renderer/texture/encode/shader_texture_encode_bc7.d.ts.map +1 -1
  446. package/src/shade/renderer/texture/encode/shader_texture_encode_bc7.js +7 -8
  447. package/src/shade/renderer/water/chunk_water_transport.d.ts.map +1 -1
  448. package/src/shade/renderer/water/chunk_water_transport.js +7 -5
  449. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -1
  450. package/src/shade/renderer/water/medium/GPUWaterBodies.js +8 -2
  451. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts +3 -2
  452. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.d.ts.map +1 -1
  453. package/src/shade/renderer/water/optics/chunk_water_receiver_downwelling.js +3 -2
  454. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  455. package/src/shade/renderer/water/shader_water.js +3 -1
  456. package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts.map +1 -1
  457. package/src/shade/renderer/water/surface/GPUWaterSurface.js +3 -1
  458. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -1
  459. package/src/shade/renderer/water/surface/graph_water_tessellation.js +11 -4
  460. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -1
  461. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +37 -20
  462. package/src/shade/wgsl/validate_wgsl_source.d.ts.map +1 -1
  463. package/src/shade/wgsl/validate_wgsl_source.js +74 -3
  464. package/src/engine/graphics3/register_instance_animation.d.ts +0 -114
  465. package/src/engine/graphics3/register_instance_animation.d.ts.map +0 -1
  466. package/src/engine/graphics3/register_instance_animation.js +0 -201
@@ -1,24 +1,35 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
- import { quat3_multiply_array } from "../../../../core/geom/3d/quaternion/quat3_multiply.js";
3
- import { quat3_nlerp_array } from "../../../../core/geom/3d/quaternion/quat3_nlerp.js";
2
+ import { array_range_equal_strict } from "../../../../core/collection/array/array_range_equal_strict.js";
3
+ import { pose3_apply_point_array } from "../../../../core/geom/3d/pose/pose3_apply_point_array.js";
4
+ import { pose3_to_local_array } from "../../../../core/geom/3d/pose/pose3_to_local_array.js";
5
+ import { quat3_clamp_angle_array } from "../../../../core/geom/3d/quaternion/quat3_clamp_angle_array.js";
6
+ import {
7
+ quat3_multiply_conjugate_right_array
8
+ } from "../../../../core/geom/3d/quaternion/quat3_multiply_conjugate_right_array.js";
4
9
  import { v3_quaternion_apply_array } from "../../../../core/geom/vec3/v3_quaternion_apply.js";
5
10
  import { v3_quaternion_apply_inverse_array } from "../../../../core/geom/vec3/v3_quaternion_apply_inverse.js";
6
11
  import { EntityObserver } from "../../../ecs/EntityObserver.js";
12
+ import { EventType } from "../../../ecs/EventType.js";
13
+ import { GPUStateAuthorityFlags } from "../../../ecs/gpu/GPUStateAuthorityFlags.js";
14
+ import { gpu_authority_has } from "../../../ecs/gpu/gpu_authority_has.js";
15
+ import { parent_entity_model_of } from "../../../ecs/parent/parent_entity_model_of.js";
7
16
  import {
8
- TransformAttachment
9
- } from "../../../ecs/transform-attachment/TransformAttachment.js";
17
+ transform_attachment_parent_of
18
+ } from "../../../ecs/transform-attachment/transform_attachment_parent_of.js";
10
19
  import {
11
20
  TransformAttachmentSystem
12
21
  } from "../../../ecs/transform-attachment/TransformAttachmentSystem.js";
13
22
  import {
14
23
  TRANSFORM64_ROTATION_OFFSET,
15
- TRANSFORM64_TRANSLATION_OFFSET
24
+ TRANSFORM64_TRANSLATION_OFFSET,
25
+ Transform64
16
26
  } from "../../../ecs/transform/Transform64.js";
17
27
  import { t64_evaluate_world } from "../../../ecs/transform/t64_evaluate_world.js";
28
+ import { t64_read_pose_array } from "../../../ecs/transform/t64_read_pose_array.js";
18
29
  import {
19
30
  collect_entity_playbacks
20
31
  } from "../../../graphics3/pose/collect_entity_playbacks.js";
21
- import { MeshSystem } from "../../../graphics3/MeshSystem.js";
32
+ import { MeshSkin } from "../../../graphics/ecs/mesh-v2/MeshSkin.js";
22
33
  import { CLOTH_SELF_RADIUS_MAX_RATIO, CLOTH_SELF_RADIUS_MIN_RATIO } from "../solver/cloth_solver_constants.js";
23
34
  import { cloth_air_changed, cloth_sample_air } from "../wind/cloth_sample_air.js";
24
35
  import { ClothStepParam } from "../solver/cloth_step_instance.js";
@@ -26,11 +37,14 @@ import { ClothStepResult } from "../solver/ClothState.js";
26
37
  import { ClothColliderSystem } from "./ClothColliderSystem.js";
27
38
  import { ClothDynamicsFlags } from "./ClothDynamicsFlags.js";
28
39
  import { ClothFlags } from "./ClothFlags.js";
40
+ import { ClothHold } from "./ClothHold.js";
29
41
  import { ClothInstance } from "./ClothInstance.js";
42
+ import { ClothPin } from "./ClothPin.js";
43
+ import { cloth_apply_holds, cloth_capture_hold, cloth_hold_step } from "./cloth_holds.js";
30
44
  import { ClothRig } from "./ClothRig.js";
31
45
  import {
32
- CLOTH_ANCHOR_ROTATION_LIMIT,
33
- CLOTH_ANCHOR_TRAVEL_REACH,
46
+ CLOTH_FRAME_ROTATION_LIMIT,
47
+ CLOTH_FRAME_TRAVEL_REACH,
34
48
  CLOTH_SLEEP_DISPLACEMENT_RATIO,
35
49
  CLOTH_SLEEP_SPEED_RATIO,
36
50
  CLOTH_SLEEP_STEPS,
@@ -61,12 +75,9 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
61
75
  * # Every cloth in a scene, and everything a step needs that is not arithmetic
62
76
  *
63
77
  * One {@link ClothInstance} per {@link Cloth} component, stepped at the fixed
64
- * rate. Per step, per cloth: move the local frame with the anchor, point the
65
- * leash rows at where they measure from, gather the colliders that could touch
66
- * it, step, and put the result back on the subtree. The animated pose the
67
- * leashes are measured against is not recomposed per step — in the entity path
68
- * it is constant in the anchor's own frame, and `cloth_seed_subtree.js` bakes
69
- * it.
78
+ * rate. Per step, per cloth: take where its holders are, move the local frame,
79
+ * point the leash rows at what they measure from, gather the colliders that
80
+ * could touch it, step, and put the result back on the subtree.
70
81
  *
71
82
  * ## Why this is not a `System`
72
83
  *
@@ -101,11 +112,11 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
101
112
  *
102
113
  * ## The local frame and the inertia model — §7 P7
103
114
  *
104
- * Particles live in the anchor's own frame. When the anchor moves, the cloth is
115
+ * Particles live in the cloth's frame. When the frame moves, the cloth is
105
116
  * asked how much of that motion it would like to feel:
106
117
  * `ClothDynamics#inertia = 1` keeps every particle's **world** position and
107
118
  * velocity, so a cape trails a sprinting character; `0` keeps their **local**
108
- * ones, so the cloth rides the anchor as though it were standing still. The
119
+ * ones, so the cloth rides the frame as though it were standing still. The
109
120
  * blend between the two is a lerp in the local frame, which is all the frame
110
121
  * change is.
111
122
  *
@@ -127,14 +138,32 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
127
138
  * step's drag response is zero, `cloth_step` takes the uniform-gravity path,
128
139
  * and a scene produces the bytes M5 measured.
129
140
  *
141
+ * ## Holds
142
+ *
143
+ * A {@link ClothPin} on any entity holds the particles it names. This world
144
+ * keeps one {@link ClothHold} per pin, by the cloth entity it names, for as long
145
+ * as the pin exists — **outside** the seeded instance, because the offsets a
146
+ * hold takes when it begins are the hold's and must survive a re-seed. A pin is
147
+ * only ever *noticed* by the observer; everything it changes in a cloth's state
148
+ * is written in {@link advance}, which is the one window in which the main
149
+ * thread owns that state under both systems.
150
+ *
151
+ * Each step, before anything else, every holder's pose is taken — read off its
152
+ * `Transform64` when the CPU owns it, evaluated exactly when a clip does — and
153
+ * expressed in the cloth's frame. `cloth_holds.js` turns those poses into
154
+ * where the held particles go, the animated pose hung from them, and the leash
155
+ * centres.
156
+ *
130
157
  * ## Teleport — §7 P6
131
158
  *
132
- * Anchor motion past a threshold is not locomotion and is not treated as any
133
- * amount of it. `ClothFlags.TeleportKeep` carries the shape across, which in a
134
- * local frame is literally doing nothing; the default re-seeds at the animated
135
- * pose with zero velocity and opens a stabilisation window in which the
136
- * velocity limit ramps back from nothing, so the first steps after a reset
137
- * cannot produce a whip.
159
+ * Frame motion past a threshold is not locomotion and is not treated as any
160
+ * amount of it, and neither is a holder jumping that far in the frame — a ship
161
+ * the rope is tied to, moved across the map. `ClothFlags.TeleportKeep` carries
162
+ * the shape across: for the frame that is literally doing nothing, and for a
163
+ * holder it is that holder's own jump applied to what hangs from it. The default
164
+ * re-seeds at the animated pose with zero velocity and opens a stabilisation
165
+ * window in which the velocity limit ramps back from nothing, so the first steps
166
+ * after a reset cannot produce a whip.
138
167
  *
139
168
  * @author Alex Goldring
140
169
  * @copyright Company Named Limited (c) 2026
@@ -162,24 +191,6 @@ export class ClothWorld {
162
191
  */
163
192
  instances = [];
164
193
 
165
- /**
166
- * Where a skinned model instance comes from. Anything that answers
167
- * `instance_of(entity)` with a `PrefabInstance` — skins whose joints
168
- * are entities — will do, and {@link MeshSystem} is what does in a
169
- * running engine.
170
- *
171
- * Public and assignable because a garment's joints are a *rendering*
172
- * structure and `MeshSystem` needs a graphics engine, a scene and a loader
173
- * to exist — so a spec that wants joints without a renderer sets this
174
- * instead, and gets exactly the interface the system uses.
175
- *
176
- * `null` means no rig can seed, which is the same additive shape the
177
- * collider index has: a scene without one gets M1's cloth rather than an
178
- * error.
179
- * @type {{instance_of: function(number): (PrefabInstance|null)}|null}
180
- */
181
- models = null;
182
-
183
194
  /**
184
195
  * @type {EntityManager|null}
185
196
  */
@@ -292,6 +303,44 @@ export class ClothWorld {
292
303
  this
293
304
  );
294
305
 
306
+ /**
307
+ * Every {@link ClothPin} in the dataset, as the hold it makes. A pin is
308
+ * noticed here and nowhere else; what it does to a cloth is written in
309
+ * {@link advance}.
310
+ * @type {EntityObserver}
311
+ * @private
312
+ */
313
+ __pin_observer = new EntityObserver(
314
+ [ClothPin],
315
+ (pin, entity) => this.#pin_added(pin, entity),
316
+ (pin, entity) => this.#pin_removed(pin, entity),
317
+ this
318
+ );
319
+
320
+ /**
321
+ * Holds by the cloth entity they name, each list ascending by holder — so a
322
+ * particle two pins name goes to the lower holder, every time.
323
+ * @type {Map<number, ClothHold[]>}
324
+ * @private
325
+ */
326
+ #holds = new Map();
327
+
328
+ /**
329
+ * Bumped for a cloth entity whenever a hold on it appears or goes;
330
+ * {@link ClothInstance#holds_version} is what an instance last applied.
331
+ * @type {Map<number, number>}
332
+ * @private
333
+ */
334
+ #hold_versions = new Map();
335
+
336
+ /**
337
+ * The dataset the observers are attached to, for the one thing a pin needs
338
+ * done outside a step: going when its cloth's entity does.
339
+ * @type {EntityComponentDataset|null}
340
+ * @private
341
+ */
342
+ #dataset = null;
343
+
295
344
  /**
296
345
  * Resolve the systems a cloth world reads. Called from the owning system's
297
346
  * `startup`.
@@ -311,10 +360,6 @@ export class ClothWorld {
311
360
  // Optional by design. A scene with no ClothColliderSystem gets cloth
312
361
  // that does not collide, rather than cloth that throws.
313
362
  this.colliders = entityManager.getSystem(ClothColliderSystem) ?? null;
314
-
315
- if (this.models === null) {
316
- this.models = entityManager.getSystem(MeshSystem) ?? null;
317
- }
318
363
  }
319
364
 
320
365
  /**
@@ -347,7 +392,18 @@ export class ClothWorld {
347
392
  * @returns {void}
348
393
  */
349
394
  attach_dataset(dataset) {
395
+ this.#dataset = dataset;
396
+
350
397
  dataset.addObserver(this.__rig_observer, true);
398
+
399
+ // A cloth simulates fine with nothing holding it, so a scene need not
400
+ // have registered the component to have cloth in it.
401
+ dataset.registerComponentType(ClothPin);
402
+
403
+ // nor a skin, to have cloth that is not a rig
404
+ dataset.registerComponentType(MeshSkin);
405
+
406
+ dataset.addObserver(this.__pin_observer, true);
351
407
  }
352
408
 
353
409
  /**
@@ -356,6 +412,378 @@ export class ClothWorld {
356
412
  */
357
413
  detach_dataset(dataset) {
358
414
  dataset.removeObserver(this.__rig_observer, false);
415
+ dataset.removeObserver(this.__pin_observer, false);
416
+
417
+ this.#dataset = null;
418
+ }
419
+
420
+ /**
421
+ * @param {ClothPin} pin
422
+ * @param {number} holder
423
+ * @private
424
+ */
425
+ #pin_added(pin, holder) {
426
+ const cloth = pin.cloth;
427
+
428
+ let list = this.#holds.get(cloth);
429
+
430
+ if (list === undefined) {
431
+ list = [];
432
+
433
+ this.#holds.set(cloth, list);
434
+
435
+ // A pin names its cloth's entity, and an entity id is reused: when
436
+ // that entity goes, the pins naming it go with it, rather than
437
+ // waiting to hold whatever is spawned under the same id next. The
438
+ // same contract `IKGoal` has with its target.
439
+ const dataset = this.#dataset;
440
+
441
+ if (dataset !== null && dataset.entityExists(cloth)) {
442
+ dataset.addEntityEventListener(cloth, EventType.EntityRemoved, this.#cloth_entity_removed, this);
443
+ }
444
+ }
445
+
446
+ const hold = new ClothHold();
447
+
448
+ hold.holder = holder;
449
+ hold.pin = pin;
450
+
451
+ let at = list.length;
452
+
453
+ while (at > 0 && list[at - 1].holder > holder) {
454
+ at--;
455
+ }
456
+
457
+ list.splice(at, 0, hold);
458
+
459
+ this.#bump(cloth);
460
+ }
461
+
462
+ /**
463
+ * @param {ClothPin} pin
464
+ * @param {number} holder
465
+ * @private
466
+ */
467
+ #pin_removed(pin, holder) {
468
+ const cloth = pin.cloth;
469
+
470
+ const list = this.#holds.get(cloth);
471
+
472
+ if (list === undefined) {
473
+ return;
474
+ }
475
+
476
+ for (let i = 0; i < list.length; i++) {
477
+ if (list[i].holder === holder) {
478
+ list.splice(i, 1);
479
+
480
+ break;
481
+ }
482
+ }
483
+
484
+ if (list.length === 0) {
485
+ this.#holds.delete(cloth);
486
+
487
+ const dataset = this.#dataset;
488
+
489
+ if (dataset !== null && dataset.entityExists(cloth)) {
490
+ dataset.removeEntityEventListener(cloth, EventType.EntityRemoved, this.#cloth_entity_removed, this);
491
+ }
492
+ }
493
+
494
+ this.#bump(cloth);
495
+ }
496
+
497
+ /**
498
+ * A cloth's entity has gone: take every pin naming it off its holder.
499
+ *
500
+ * @param {*} event
501
+ * @param {number} cloth
502
+ * @private
503
+ */
504
+ #cloth_entity_removed(event, cloth) {
505
+ const list = this.#holds.get(cloth);
506
+
507
+ const dataset = this.#dataset;
508
+
509
+ if (list === undefined || dataset === null) {
510
+ return;
511
+ }
512
+
513
+ // Removing a pin edits the list, so walk a copy.
514
+ const holders = list.map(hold => hold.holder);
515
+
516
+ for (let i = 0; i < holders.length; i++) {
517
+ if (dataset.entityExists(holders[i])) {
518
+ dataset.removeComponentFromEntity(holders[i], ClothPin);
519
+ }
520
+ }
521
+ }
522
+
523
+ /**
524
+ * @param {number} cloth
525
+ * @private
526
+ */
527
+ #bump(cloth) {
528
+ this.#hold_versions.set(cloth, (this.#hold_versions.get(cloth) ?? 0) + 1);
529
+
530
+ const instance = this.__by_entity.get(cloth);
531
+
532
+ if (instance !== undefined) {
533
+ instance.asleep = false;
534
+ instance.still_steps = 0;
535
+ }
536
+ }
537
+
538
+ /**
539
+ * The particles a cloth has and the entities they are, enough to say
540
+ * whether a holder hangs from one of them.
541
+ *
542
+ * A holder that cloth itself moves — a link of the same rope, or anything
543
+ * hanging below one — would chase its own write-back one step behind, so it
544
+ * is refused once, by name, and holds nothing. The cloth's own entity is
545
+ * allowed: for a subtree it is the frame, which cloth reads and never writes.
546
+ *
547
+ * @param {ClothInstance} instance
548
+ * @param {ClothHold} hold
549
+ * @returns {string} empty when the hold may hold, otherwise why not
550
+ * @private
551
+ */
552
+ #refusal(instance, hold) {
553
+ const ecd = this.entityManager.dataset;
554
+
555
+ for (let entity = hold.holder, depth = 0; entity >= 0 && depth < 256; depth++) {
556
+ if (this.#is_output(instance, entity)) {
557
+ return `entity ${hold.holder} holds cloth ${instance.entity} and hangs from entity ${entity}, which that cloth moves`;
558
+ }
559
+
560
+ entity = transform_attachment_parent_of(ecd, entity);
561
+ }
562
+
563
+ const particles = hold.pin.particles;
564
+
565
+ for (let k = 0; k < particles.length; k++) {
566
+ if (particles[k] >= instance.node_count) {
567
+ return `entity ${hold.holder} holds particle ${particles[k]} of cloth ${instance.entity}, which has ${instance.node_count}`;
568
+ }
569
+ }
570
+
571
+ return "";
572
+ }
573
+
574
+ /**
575
+ * @param {ClothInstance} instance
576
+ * @param {number} entity
577
+ * @returns {boolean} whether cloth writes this entity's pose
578
+ * @private
579
+ */
580
+ #is_output(instance, entity) {
581
+ const output = instance.node_output;
582
+ const entities = instance.rig === null ? instance.node_entity : instance.node_joint;
583
+
584
+ for (let node = 0; node < instance.node_count; node++) {
585
+ if (output[node] !== 0 && entities[node] === entity) {
586
+ return true;
587
+ }
588
+ }
589
+
590
+ return false;
591
+ }
592
+
593
+ /**
594
+ * Bring an instance's holds up to date with the pins that name it: pick up a
595
+ * changed set, take the offsets of any hold whose holder has just been found,
596
+ * and re-derive whatever holding decides. Nothing here runs unless something
597
+ * changed.
598
+ *
599
+ * @param {ClothInstance} instance
600
+ * @param {Float64Array} frame_translation the frame the particle positions are in
601
+ * @param {Float64Array} frame_rotation
602
+ * @returns {boolean} whether anything about what holds the cloth changed
603
+ * @private
604
+ */
605
+ #refresh_holds(instance, frame_translation, frame_rotation) {
606
+ let changed = false;
607
+
608
+ const holds = instance.holds;
609
+
610
+ for (let h = 0; h < holds.length; h++) {
611
+ const hold = holds[h];
612
+
613
+ if (!hold.captured && hold.valid && !hold.refused) {
614
+ cloth_capture_hold(instance, hold, frame_translation, frame_rotation);
615
+
616
+ changed = true;
617
+ }
618
+ }
619
+
620
+ if (changed || instance.holds_applied !== instance.holds_version) {
621
+ cloth_apply_holds(instance);
622
+
623
+ instance.holds_applied = instance.holds_version;
624
+
625
+ // A different set of holds is a different cloth to rest; the window
626
+ // that was open measured the old one.
627
+ instance.still_steps = 0;
628
+ instance.sleep_reference = null;
629
+
630
+ changed = true;
631
+ }
632
+
633
+ return changed;
634
+ }
635
+
636
+ /**
637
+ * Pick up a changed set of pins: the instance's holds become the registry's,
638
+ * each one admitted or refused.
639
+ *
640
+ * @param {ClothInstance} instance
641
+ * @private
642
+ */
643
+ #sync_holds(instance) {
644
+ const version = this.#hold_versions.get(instance.entity) ?? 0;
645
+
646
+ if (instance.holds_version === version) {
647
+ return;
648
+ }
649
+
650
+ const list = this.#holds.get(instance.entity);
651
+
652
+ instance.holds = list === undefined ? [] : list.slice();
653
+ instance.holds_version = version;
654
+
655
+ for (let h = 0; h < instance.holds.length; h++) {
656
+ const hold = instance.holds[h];
657
+
658
+ const refusal = this.#refusal(instance, hold);
659
+
660
+ hold.refused = refusal !== "";
661
+
662
+ if (hold.refused && !hold.reported) {
663
+ hold.reported = true;
664
+
665
+ console.warn(`ClothSystem: ${refusal}`);
666
+ }
667
+ }
668
+ }
669
+
670
+ /**
671
+ * Where every holder is this step: in the world, and in the cloth's frame.
672
+ *
673
+ * @param {ClothInstance} instance
674
+ * @param {Float64Array} frame_translation this step's frame
675
+ * @param {Float64Array} frame_rotation
676
+ * @returns {boolean} whether any holder moved, appeared or vanished since
677
+ * the last step
678
+ * @private
679
+ */
680
+ #evaluate_holds(instance, frame_translation, frame_rotation) {
681
+ let moved = false;
682
+
683
+ const holds = instance.holds;
684
+
685
+ for (let h = 0; h < holds.length; h++) {
686
+ const hold = holds[h];
687
+
688
+ const was_valid = hold.valid;
689
+
690
+ hold.valid = !hold.refused && this.#holder_world(hold.holder, hold.world);
691
+
692
+ if (!hold.valid) {
693
+ moved = moved || was_valid;
694
+
695
+ continue;
696
+ }
697
+
698
+ const world = hold.world;
699
+ const pose = hold.pose;
700
+
701
+ // in the frame: q_f⁻¹ · (t − t_f), q_f⁻¹ ⊗ q
702
+ pose3_to_local_array(pose, 0, pose, 3, frame_translation, 0, frame_rotation, 0, world, 0, world, 3);
703
+
704
+ if (!hold.has_previous) {
705
+ moved = true;
706
+
707
+ continue;
708
+ }
709
+
710
+ if (!array_range_equal_strict(world, 0, hold.world_previous, 0, 7)) {
711
+ moved = true;
712
+ }
713
+ }
714
+
715
+ return moved;
716
+ }
717
+
718
+ /**
719
+ * A holder's world pose, `[tx, ty, tz, qx, qy, qz, qw]`.
720
+ *
721
+ * Read off its `Transform64` when the CPU owns that, which is every prop and
722
+ * every hand a script moves. A transform the GPU composes — a bone a clip
723
+ * drives — holds the rest pose under wherever the CPU last composed it, so
724
+ * it is evaluated exactly instead, under the clips of every model on its
725
+ * chain: a pole in a hand belongs to one model and hangs from another's
726
+ * bones, and evaluating it under only its own would put it where the hand
727
+ * was in the bind pose.
728
+ *
729
+ * @param {number} entity
730
+ * @param {Float64Array} out
731
+ * @returns {boolean} false when the entity has no pose to give yet — gone,
732
+ * or hanging from a parent that has no transform
733
+ * @private
734
+ */
735
+ #holder_world(entity, out) {
736
+ const ecd = this.entityManager.dataset;
737
+
738
+ if (!ecd.entityExists(entity)) {
739
+ return false;
740
+ }
741
+
742
+ const transform = ecd.getComponent(entity, Transform64);
743
+
744
+ if (transform === undefined) {
745
+ return false;
746
+ }
747
+
748
+ let source = transform;
749
+
750
+ if (gpu_authority_has(ecd, entity, GPUStateAuthorityFlags.Transform64)) {
751
+ const playbacks = PLAYBACKS;
752
+
753
+ playbacks.length = 0;
754
+
755
+ // every model on the chain, each once; and a chain that is not
756
+ // complete yet is not one to evaluate
757
+ MODELS.length = 0;
758
+
759
+ for (let e = entity, depth = 0; e >= 0 && depth < 256; depth++) {
760
+ if (!ecd.entityExists(e) || ecd.getComponent(e, Transform64) === undefined) {
761
+ playbacks.length = 0;
762
+
763
+ return false;
764
+ }
765
+
766
+ const model = parent_entity_model_of(ecd, e);
767
+
768
+ if (MODELS.indexOf(model) === -1) {
769
+ MODELS.push(model);
770
+
771
+ collect_entity_playbacks(playbacks, this.entityManager, model);
772
+ }
773
+
774
+ e = transform_attachment_parent_of(ecd, e);
775
+ }
776
+
777
+ t64_evaluate_world(EVALUATED, ecd, entity, playbacks);
778
+
779
+ playbacks.length = 0;
780
+
781
+ source = EVALUATED;
782
+ }
783
+
784
+ t64_read_pose_array(out, 0, source);
785
+
786
+ return true;
359
787
  }
360
788
 
361
789
  /**
@@ -489,13 +917,14 @@ export class ClothWorld {
489
917
  }
490
918
 
491
919
  instance.seeded = false;
492
- instance.anchor_valid = false;
920
+ instance.frame_valid = false;
493
921
 
494
922
  return instance;
495
923
  }
496
924
 
497
925
  /**
498
- * Wake a sleeping cloth. Anchor motion and a `ClothDynamics` change do this
926
+ * Wake a sleeping cloth. Frame motion, a holder moving or a hold changing,
927
+ * and a `ClothDynamics` change do this
499
928
  * on their own; a caller that has changed the world in some way the cloth
500
929
  * cannot see — M6's wind, for one — says so here.
501
930
  *
@@ -519,6 +948,37 @@ export class ClothWorld {
519
948
  return this.__by_entity.get(entity);
520
949
  }
521
950
 
951
+ /**
952
+ * Which particle of a cloth an entity is: a link of a subtree cloth, or a
953
+ * joint of a rigged one — the number a {@link ClothPin} names it by.
954
+ *
955
+ * For code that has the entity rather than the number, which is what a hand
956
+ * closing on a rope has. It answers once the cloth has seeded, which is its
957
+ * first step.
958
+ *
959
+ * @param {number} cloth the entity carrying the `Cloth`
960
+ * @param {number} entity
961
+ * @returns {number} the particle, or `−1` when the entity is not one of the
962
+ * cloth's, or the cloth has not seeded yet
963
+ */
964
+ particle_of(cloth, entity) {
965
+ const instance = this.__by_entity.get(cloth);
966
+
967
+ if (instance === undefined || !instance.seeded) {
968
+ return -1;
969
+ }
970
+
971
+ const entities = instance.rig === null ? instance.node_entity : instance.node_joint;
972
+
973
+ for (let node = 0; node < instance.node_count; node++) {
974
+ if (entities[node] === entity) {
975
+ return node;
976
+ }
977
+ }
978
+
979
+ return -1;
980
+ }
981
+
522
982
  /**
523
983
  * Everything that happens once per tick rather than once per cloth.
524
984
  *
@@ -640,9 +1100,9 @@ export class ClothWorld {
640
1100
  * simulates the joints of the skinned model the entity is showing. There is
641
1101
  * no mode field anywhere, and the solver does not know which ran.
642
1102
  *
643
- * A rig can fail to seed for reasons that are not errors — the model has
644
- * not loaded, or nothing in the manager hands out model instances — and one
645
- * that does is left unseeded for the next step to try again. A rig that
1103
+ * A rig can fail to seed for reasons that are not errors — it names no skin
1104
+ * yet, or the entity it names carries none yet, a model still loading — and
1105
+ * one that does is left unseeded for the next step to try again. A rig that
646
1106
  * fails for a reason that *is* an error says so once and then stops, because
647
1107
  * a proxy that does not fit its skin will not start fitting it.
648
1108
  *
@@ -659,6 +1119,10 @@ export class ClothWorld {
659
1119
 
660
1120
  cloth_seed_subtree(instance, ecd, this.attachments, dt);
661
1121
 
1122
+ // A new state holds nothing yet: the holds — offsets and all — are
1123
+ // applied to it on its first step.
1124
+ instance.holds_applied = -1;
1125
+
662
1126
  return;
663
1127
  }
664
1128
 
@@ -673,21 +1137,16 @@ export class ClothWorld {
673
1137
  return;
674
1138
  }
675
1139
 
676
- if (this.models === null) {
677
- this.#refuse(instance, `entity ${instance.entity} has a ClothRig and nothing hands out model instances`);
1140
+ const skin = rig.skin === -1 || !ecd.entityExists(rig.skin)
1141
+ ? undefined
1142
+ : ecd.getComponent(rig.skin, MeshSkin);
678
1143
 
1144
+ if (skin === undefined) {
1145
+ // Not named yet, or not there yet: still loading. Not an error, and
1146
+ // not worth a message.
679
1147
  return;
680
1148
  }
681
1149
 
682
- const model = this.models.instance_of(instance.entity);
683
-
684
- if (model === null || model === undefined) {
685
- // Still loading. Not an error, and not worth a message.
686
- return;
687
- }
688
-
689
- const skin = model.skins[rig.skin];
690
-
691
1150
  const error = rig.proxy.checkAgainst(skin);
692
1151
 
693
1152
  if (error !== "") {
@@ -699,6 +1158,8 @@ export class ClothWorld {
699
1158
  instance.rig = rig;
700
1159
 
701
1160
  cloth_seed_rig(instance, rig.proxy, skin.joints, ecd, dt);
1161
+
1162
+ instance.holds_applied = -1;
702
1163
  }
703
1164
 
704
1165
  /**
@@ -720,8 +1181,9 @@ export class ClothWorld {
720
1181
 
721
1182
  /**
722
1183
  * Carry one seeded cloth to the edge of its step and write the parameters
723
- * the step will run on: whatever the anchor did to the local frame, where
724
- * the leash rows measure from, and the colliders within reach.
1184
+ * the step will run on: whatever the frame and the holders did, where the
1185
+ * held particles go, where the leash rows measure from, and the colliders
1186
+ * within reach.
725
1187
  *
726
1188
  * @param {ClothInstance} instance
727
1189
  * @param {number} dt seconds
@@ -760,7 +1222,7 @@ export class ClothWorld {
760
1222
 
761
1223
  const transform = instance.rig === null
762
1224
  ? instance.transform
763
- : this.#anchor_pose(instance);
1225
+ : this.#frame_pose(instance);
764
1226
 
765
1227
  const tx = transform[TRANSFORM64_TRANSLATION_OFFSET];
766
1228
  const ty = transform[TRANSFORM64_TRANSLATION_OFFSET + 1];
@@ -771,15 +1233,38 @@ export class ClothWorld {
771
1233
  const qz = transform[TRANSFORM64_ROTATION_OFFSET + 2];
772
1234
  const qw = transform[TRANSFORM64_ROTATION_OFFSET + 3];
773
1235
 
774
- const previous_translation = instance.anchor_translation;
775
- const previous_rotation = instance.anchor_rotation;
1236
+ const previous_translation = instance.frame_translation;
1237
+ const previous_rotation = instance.frame_rotation;
1238
+
1239
+ FRAME_TRANSLATION[0] = tx;
1240
+ FRAME_TRANSLATION[1] = ty;
1241
+ FRAME_TRANSLATION[2] = tz;
1242
+
1243
+ FRAME_ROTATION[0] = qx;
1244
+ FRAME_ROTATION[1] = qy;
1245
+ FRAME_ROTATION[2] = qz;
1246
+ FRAME_ROTATION[3] = qw;
1247
+
1248
+ // What holds the cloth, before anything that might decide it is asleep:
1249
+ // a hold appearing or going is a reason to wake, and so is a holder
1250
+ // moving.
1251
+ this.#sync_holds(instance);
1252
+
1253
+ const holders_moved = this.#evaluate_holds(instance, FRAME_TRANSLATION, FRAME_ROTATION);
1254
+
1255
+ // Positions are still in the frame they were last stepped in, which on
1256
+ // a cloth's first step is this one; a hold begins where its particles
1257
+ // are, so that is the frame its offsets are taken in.
1258
+ const holds_changed = instance.frame_valid
1259
+ ? this.#refresh_holds(instance, previous_translation, previous_rotation)
1260
+ : this.#refresh_holds(instance, FRAME_TRANSLATION, FRAME_ROTATION);
776
1261
 
777
1262
  if (instance.cloth.blend !== instance.blend_seen) {
778
1263
  instance.asleep = false;
779
1264
  instance.still_steps = 0;
780
1265
  }
781
1266
 
782
- const moved = instance.anchor_valid && (
1267
+ const moved = instance.frame_valid && (
783
1268
  previous_translation[0] !== tx
784
1269
  || previous_translation[1] !== ty
785
1270
  || previous_translation[2] !== tz
@@ -789,7 +1274,7 @@ export class ClothWorld {
789
1274
  || previous_rotation[3] !== qw
790
1275
  );
791
1276
 
792
- if (moved) {
1277
+ if (moved || holders_moved || holds_changed) {
793
1278
  instance.asleep = false;
794
1279
  instance.still_steps = 0;
795
1280
  }
@@ -814,7 +1299,7 @@ export class ClothWorld {
814
1299
  // cheaper than the step it decides whether to skip.
815
1300
  if (cloth_air_changed(
816
1301
  instance.state, this.wind,
817
- instance.anchor_translation, instance.anchor_rotation,
1302
+ instance.frame_translation, instance.frame_rotation,
818
1303
  instance.air_seen, CLOTH_SLEEP_SPEED_RATIO * instance.reach,
819
1304
  instance.age
820
1305
  )) {
@@ -831,33 +1316,58 @@ export class ClothWorld {
831
1316
  return false;
832
1317
  }
833
1318
 
834
- if (moved) {
835
- const displacement = Math.hypot(tx - previous_translation[0], ty - previous_translation[1], tz - previous_translation[2]);
1319
+ // §7 P6. Locomotion moves a frame a fraction of the cloth's own reach a
1320
+ // step; a jump past this is something else, whether it is the frame
1321
+ // that jumped or something holding the cloth, measured in the frame.
1322
+ const threshold = Math.max(CLOTH_TELEPORT_REACH * instance.reach, CLOTH_TELEPORT_FLOOR);
836
1323
 
837
- const teleport = displacement > Math.max(
838
- CLOTH_TELEPORT_REACH * instance.reach,
839
- CLOTH_TELEPORT_FLOOR
840
- );
1324
+ const frame_jumped = moved && Math.hypot(
1325
+ tx - previous_translation[0], ty - previous_translation[1], tz - previous_translation[2]
1326
+ ) > threshold;
1327
+
1328
+ let holder_jumped = false;
1329
+
1330
+ const holds = instance.holds;
1331
+
1332
+ for (let h = 0; h < holds.length; h++) {
1333
+ const hold = holds[h];
1334
+
1335
+ if (!hold.valid || !hold.has_previous || !hold.captured) {
1336
+ continue;
1337
+ }
1338
+
1339
+ const pose = hold.pose;
1340
+ const before = hold.pose_previous;
1341
+
1342
+ if (Math.hypot(pose[0] - before[0], pose[1] - before[1], pose[2] - before[2]) > threshold) {
1343
+ holder_jumped = true;
1344
+ }
1345
+ }
841
1346
 
842
- if (teleport) {
843
- this.#teleport(instance);
1347
+ const keep = (instance.cloth.flags & ClothFlags.TeleportKeep) !== 0;
1348
+
1349
+ let reset = false;
1350
+
1351
+ if (frame_jumped || holder_jumped) {
1352
+ instance.teleport_count++;
1353
+
1354
+ if (keep) {
1355
+ instance.holds_dirty = true;
1356
+
1357
+ // The frame's jump needs nothing: the particles are in its frame.
1358
+ // A holder's is carried onto what hangs from it.
1359
+ for (let h = 0; h < holds.length; h++) {
1360
+ const hold = holds[h];
1361
+
1362
+ if (hold.valid && hold.has_previous && hold.captured) {
1363
+ carry_with_holder(instance, h);
1364
+ }
1365
+ }
844
1366
  } else {
845
- // the new pose goes in as an array: handed over as seven numbers, each was boxed
846
- // wherever the call was not inlined
847
- ANCHOR_POSE[0] = tx;
848
- ANCHOR_POSE[1] = ty;
849
- ANCHOR_POSE[2] = tz;
850
- ANCHOR_POSE[3] = qx;
851
- ANCHOR_POSE[4] = qy;
852
- ANCHOR_POSE[5] = qz;
853
- ANCHOR_POSE[6] = qw;
854
-
855
- move_local_frame(instance, ANCHOR_POSE, previous_translation, previous_rotation);
1367
+ reset = true;
856
1368
  }
857
- } else if (instance.anchor_valid && instance.cloth.dynamics.inertia > 0) {
858
- // The frame is identical. Preserve the pinning side effect of
859
- // move_local_frame without rewriting every free position/velocity.
860
- pin_anchor(instance);
1369
+ } else if (moved) {
1370
+ move_local_frame(instance, FRAME_TRANSLATION, FRAME_ROTATION, previous_translation, previous_rotation);
861
1371
  }
862
1372
 
863
1373
  previous_translation[0] = tx;
@@ -869,13 +1379,30 @@ export class ClothWorld {
869
1379
  previous_rotation[2] = qz;
870
1380
  previous_rotation[3] = qw;
871
1381
 
872
- instance.anchor_valid = true;
1382
+ instance.frame_valid = true;
1383
+
1384
+ // Where every held particle goes, the pose hung from them, the leash
1385
+ // centres — from this step's holder poses.
1386
+ cloth_hold_step(instance);
1387
+
1388
+ if (reset) {
1389
+ this.#reset(instance);
1390
+ }
1391
+
1392
+ for (let h = 0; h < holds.length; h++) {
1393
+ const hold = holds[h];
1394
+
1395
+ if (hold.valid) {
1396
+ hold.pose_previous.set(hold.pose);
1397
+ hold.world_previous.set(hold.world);
873
1398
 
874
- refresh_leash_targets(instance);
1399
+ hold.has_previous = true;
1400
+ }
1401
+ }
875
1402
 
876
1403
  const dynamics = instance.cloth.dynamics;
877
1404
 
878
- // Gravity is a world vector and the solve is in the anchor's frame.
1405
+ // Gravity is a world vector and the solve is in the cloth's frame.
879
1406
  const gravity = this.gravity;
880
1407
 
881
1408
  // `previous_rotation` has just been set to this step's `q`; vector and rotation go in as arrays
@@ -894,7 +1421,8 @@ export class ClothWorld {
894
1421
  // nothing beyond it — `cloth_gather_reach` carries the derivation,
895
1422
  // and why the velocity clamp that used to stand here was both far
896
1423
  // too wide and missing two terms.
897
- const reach = cloth_gather_reach(state, this.colliders.index, dt);
1424
+ // and as far again as a holder drags it, which no velocity says
1425
+ const reach = cloth_gather_reach(state, this.colliders.index, dt) + instance.kinematic_travel;
898
1426
 
899
1427
  cloth_gather_colliders(instance, this.colliders.index, reach);
900
1428
 
@@ -921,7 +1449,7 @@ export class ClothWorld {
921
1449
 
922
1450
  instance.air_varies = cloth_sample_air(
923
1451
  state, this.wind,
924
- instance.anchor_translation, instance.anchor_rotation,
1452
+ instance.frame_translation, instance.frame_rotation,
925
1453
  instance.air_seen, instance.age
926
1454
  );
927
1455
  } else {
@@ -929,6 +1457,7 @@ export class ClothWorld {
929
1457
  }
930
1458
 
931
1459
  params[offset + ClothStepParam.DRAG_RESPONSE] = drag_response;
1460
+ params[offset + ClothStepParam.KINEMATIC_TRAVEL] = instance.kinematic_travel;
932
1461
 
933
1462
  const self_collision = (dynamics.flags & ClothDynamicsFlags.SelfCollision) !== 0;
934
1463
 
@@ -980,8 +1509,10 @@ export class ClothWorld {
980
1509
  }
981
1510
 
982
1511
  /**
983
- * The anchor joint's world pose, computed on the CPU from the clips the
984
- * entity is playing.
1512
+ * A rig's frame joint's world pose, computed on the CPU from the clips the
1513
+ * entity is playing — and, while the playbacks are in hand, the pose in that
1514
+ * frame of every bone the garment hangs from, for the write-back to express
1515
+ * the garment's roots against.
985
1516
  *
986
1517
  * **Computed, never read back**, for two reasons that are both the engine's
987
1518
  * own. A joint of an animated skeleton is the GPU's (`GPUStateAuthority`),
@@ -997,36 +1528,58 @@ export class ClothWorld {
997
1528
  * @returns {Transform64}
998
1529
  * @private
999
1530
  */
1000
- #anchor_pose(instance) {
1531
+ #frame_pose(instance) {
1001
1532
  const playbacks = PLAYBACKS;
1002
1533
 
1003
1534
  playbacks.length = 0;
1004
1535
 
1005
1536
  collect_entity_playbacks(playbacks, this.entityManager, instance.entity);
1006
1537
 
1007
- t64_evaluate_world(instance.anchor_pose, this.entityManager.dataset, instance.node_joint[0], playbacks);
1538
+ const ecd = this.entityManager.dataset;
1539
+
1540
+ const frame = instance.frame_pose;
1541
+
1542
+ t64_evaluate_world(frame, ecd, instance.frame_entity, playbacks);
1543
+
1544
+ const node_output = instance.node_output;
1545
+ const boundary = instance.boundary_pose;
1546
+
1547
+ for (let node = 0; node < instance.node_count; node++) {
1548
+ if (node_output[node] !== 0) {
1549
+ continue;
1550
+ }
1551
+
1552
+ t64_evaluate_world(EVALUATED, ecd, instance.node_joint[node], playbacks);
1553
+
1554
+ const b = node * 7;
1555
+
1556
+ pose3_to_local_array(
1557
+ boundary, b,
1558
+ boundary, b + 3,
1559
+ frame, TRANSFORM64_TRANSLATION_OFFSET,
1560
+ frame, TRANSFORM64_ROTATION_OFFSET,
1561
+ EVALUATED, TRANSFORM64_TRANSLATION_OFFSET,
1562
+ EVALUATED, TRANSFORM64_ROTATION_OFFSET
1563
+ );
1564
+ }
1008
1565
 
1009
1566
  playbacks.length = 0;
1010
1567
 
1011
- return instance.anchor_pose;
1568
+ return frame;
1012
1569
  }
1013
1570
 
1014
1571
  /**
1572
+ * §7 P6's `Reset`: every particle onto the animated pose — the rest pose
1573
+ * hung from where its holders now are — at rest, with nothing carried over.
1574
+ *
1575
+ * A held particle lands on its target, and its target is where the predict
1576
+ * carries it to, so it does not move again this step: the jump is taken
1577
+ * between steps rather than dragged through one.
1578
+ *
1015
1579
  * @param {ClothInstance} instance
1016
1580
  * @private
1017
1581
  */
1018
- #teleport(instance) {
1019
- instance.teleport_count++;
1020
-
1021
- if ((instance.cloth.flags & ClothFlags.TeleportKeep) !== 0) {
1022
- // `Keep` carries the cloth's shape across the jump. Particles are
1023
- // stored relative to the anchor, so that is exactly what happens if
1024
- // this function does nothing to them — the frame moved and they did
1025
- // not, which is MagicaCloth2's rigid delta expressed in the frame
1026
- // that makes it free.
1027
- return;
1028
- }
1029
-
1582
+ #reset(instance) {
1030
1583
  const state = instance.state;
1031
1584
 
1032
1585
  const node_count = instance.node_count;
@@ -1043,6 +1596,10 @@ export class ClothWorld {
1043
1596
  }
1044
1597
 
1045
1598
  state.position_previous.set(state.position);
1599
+ state.kinematic_target.set(state.position);
1600
+
1601
+ instance.kinematic_travel = 0;
1602
+ instance.holds_dirty = true;
1046
1603
  state.velocity.fill(0);
1047
1604
  state.inertia_blend.fill(0);
1048
1605
 
@@ -1078,54 +1635,62 @@ const LOCAL_GRAVITY = new Float64Array(3);
1078
1635
  const FRAME = new Float64Array(8);
1079
1636
 
1080
1637
  /**
1081
- * The anchor's pose this step, `[tx, ty, tz, qx, qy, qz, qw]`, as {@link move_local_frame} takes it.
1638
+ * The frame's translation and rotation this step, apart, as the holds and {@link move_local_frame}
1639
+ * take them: the transform read once, before anything this step could write it.
1082
1640
  * @type {Float64Array}
1083
1641
  * @private
1084
1642
  */
1085
- const ANCHOR_POSE = new Float64Array(7);
1643
+ const FRAME_TRANSLATION = new Float64Array(3);
1086
1644
 
1087
1645
  /**
1088
- * {@link move_local_frame}'s `q_now⁻¹`, `[-qx, -qy, -qz, qw]`.
1089
1646
  * @type {Float64Array}
1090
1647
  * @private
1091
1648
  */
1092
- const POSE_CONJUGATE = new Float64Array(4);
1649
+ const FRAME_ROTATION = new Float64Array(4);
1093
1650
 
1094
1651
  /**
1095
- * The identity rotation, `[0, 0, 0, 1]`, which {@link move_local_frame} limits a turn towards. Never
1096
- * written.
1097
- * @type {Float64Array}
1652
+ * An exactly evaluated world pose: a holder a clip drives, a bone a garment
1653
+ * hangs from.
1654
+ * @type {Transform64}
1098
1655
  * @private
1099
1656
  */
1100
- const IDENTITY_ROTATION = new Float64Array([0, 0, 0, 1]);
1657
+ const EVALUATED = new Transform64();
1101
1658
 
1102
1659
  /**
1103
- * Carry the particles across a change of anchor pose, by however much of it the
1660
+ * The models a holder's chain passes through, each once.
1661
+ * @type {number[]}
1662
+ * @private
1663
+ */
1664
+ const MODELS = [];
1665
+
1666
+ /**
1667
+ * Carry the free particles across a change of frame pose, by however much of it the
1104
1668
  * fabric is willing to feel. §7 P7.
1105
1669
  *
1106
1670
  * A particle at local `p` was at world `t_prev + q_prev·p`. Holding that world
1107
1671
  * position fixed, its local coordinates become `D + Q·p` with
1108
1672
  * `D = q_now⁻¹·(t_prev − t_now)` and `Q = q_now⁻¹ ⊗ q_prev`. Feeling *all* of
1109
- * the anchor's motion is taking that; feeling *none* is leaving `p` alone; and
1673
+ * the frame's motion is taking that; feeling *none* is leaving `p` alone; and
1110
1674
  * `inertia` is the lerp between them. Velocity is carried the same way, minus
1111
1675
  * the translation, since a translation of the frame does not rotate a vector.
1112
1676
  *
1113
1677
  * `D` and `Q` are limited before the lerp. The limits are the cloth's own reach
1114
- * per step and a fixed angle per step, which is what makes an anchor that
1678
+ * per step and a fixed angle per step, which is what makes a frame that
1115
1679
  * teleports a metre — or spins — something a cape *lags behind* rather than
1116
1680
  * something it is fired out of.
1117
1681
  *
1118
1682
  * Every vector and quaternion goes to its function as an array and an offset: handed over as numbers,
1119
1683
  * each was boxed into a heap object wherever the call was not inlined, which until V8 optimises this
1120
- * is every call, twice per particle per step of a moving anchor.
1684
+ * is every call, twice per particle per step of a moving frame.
1121
1685
  *
1122
1686
  * @param {ClothInstance} instance
1123
- * @param {Float64Array} pose the anchor's pose now, `[tx, ty, tz, qx, qy, qz, qw]`
1687
+ * @param {Float64Array} translation the frame's translation now
1688
+ * @param {Float64Array} rotation the frame's rotation now
1124
1689
  * @param {Float64Array} previous_translation
1125
1690
  * @param {Float64Array} previous_rotation
1126
1691
  * @private
1127
1692
  */
1128
- function move_local_frame(instance, pose, previous_translation, previous_rotation) {
1693
+ function move_local_frame(instance, translation, rotation, previous_translation, previous_rotation) {
1129
1694
  let inertia = instance.cloth.dynamics.inertia;
1130
1695
 
1131
1696
  if (inertia <= 0) {
@@ -1136,13 +1701,10 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
1136
1701
  inertia = 1;
1137
1702
  }
1138
1703
 
1139
- FRAME[0] = previous_translation[0] - pose[0];
1140
- FRAME[1] = previous_translation[1] - pose[1];
1141
- FRAME[2] = previous_translation[2] - pose[2];
1142
-
1143
- v3_quaternion_apply_inverse_array(FRAME, 0, FRAME, 0, pose, 3);
1704
+ // the previous pose in the frame now: D and Q
1705
+ pose3_to_local_array(FRAME, 0, FRAME, 4, translation, 0, rotation, 0, previous_translation, 0, previous_rotation, 0);
1144
1706
 
1145
- const travel_limit = CLOTH_ANCHOR_TRAVEL_REACH * instance.reach;
1707
+ const travel_limit = CLOTH_FRAME_TRAVEL_REACH * instance.reach;
1146
1708
 
1147
1709
  const travel = Math.hypot(FRAME[0], FRAME[1], FRAME[2]);
1148
1710
 
@@ -1154,21 +1716,10 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
1154
1716
  FRAME[2] *= scale;
1155
1717
  }
1156
1718
 
1157
- POSE_CONJUGATE[0] = -pose[3];
1158
- POSE_CONJUGATE[1] = -pose[4];
1159
- POSE_CONJUGATE[2] = -pose[5];
1160
- POSE_CONJUGATE[3] = pose[6];
1161
-
1162
- quat3_multiply_array(FRAME, 4, POSE_CONJUGATE, 0, previous_rotation, 0);
1163
-
1164
- // `|w|` is `cos(θ/2)`, so this is the angle test without an `acos`.
1165
- const half_angle_cos = FRAME[7] < 0 ? -FRAME[7] : FRAME[7];
1166
-
1167
- if (half_angle_cos < Math.cos(CLOTH_ANCHOR_ROTATION_LIMIT * 0.5)) {
1168
- const angle = 2 * Math.acos(half_angle_cos > 1 ? 1 : half_angle_cos);
1169
-
1170
- quat3_nlerp_array(FRAME, 4, IDENTITY_ROTATION, 0, FRAME, 4, CLOTH_ANCHOR_ROTATION_LIMIT / angle);
1171
- }
1719
+ // Exactly the limit about the same axis. An nlerp from the identity by `limit / θ` follows the
1720
+ // chord and turned less than the limit the further past it the frame spun: three quarters of it
1721
+ // at a half turn.
1722
+ quat3_clamp_angle_array(FRAME, 4, FRAME, 4, CLOTH_FRAME_ROTATION_LIMIT);
1172
1723
 
1173
1724
  const state = instance.state;
1174
1725
 
@@ -1181,7 +1732,16 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
1181
1732
  const dy = FRAME[1];
1182
1733
  const dz = FRAME[2];
1183
1734
 
1735
+ const mass_inverse = state.mass_inverse;
1736
+
1184
1737
  for (let i = 0; i < particle_count; i++) {
1738
+ // A held particle belongs to its holder, not to the world: it goes where
1739
+ // its holder takes it, through the step, and not where the frame's
1740
+ // motion would have left it.
1741
+ if (mass_inverse[i] === 0) {
1742
+ continue;
1743
+ }
1744
+
1185
1745
  const p = i * 3;
1186
1746
 
1187
1747
  v3_quaternion_apply_array(CARRIED, 0, position, p, FRAME, 4);
@@ -1196,30 +1756,60 @@ function move_local_frame(instance, pose, previous_translation, previous_rotatio
1196
1756
  velocity[p + 1] += inertia * (CARRIED[1] - velocity[p + 1]);
1197
1757
  velocity[p + 2] += inertia * (CARRIED[2] - velocity[p + 2]);
1198
1758
  }
1199
-
1200
- pin_anchor(instance);
1201
1759
  }
1202
1760
 
1203
1761
  /**
1762
+ * `ClothFlags.TeleportKeep` for a holder that jumped: its own jump, in the
1763
+ * frame, applied to everything hanging from it — the particles it holds and
1764
+ * every free one whose nearest held particle is one of them — positions and
1765
+ * velocities alike. What hangs from another holder stays where it is.
1766
+ *
1204
1767
  * @param {ClothInstance} instance
1768
+ * @param {number} h index into {@link ClothInstance#holds}
1205
1769
  * @private
1206
1770
  */
1207
- function pin_anchor(instance) {
1208
- const position = instance.state.position;
1209
- const velocity = instance.state.velocity;
1210
- // The anchor is pinned and is the origin of the frame it anchors, so it is
1211
- // the one particle that must not be carried anywhere.
1212
- const anchor = instance.particle_of_node[0] * 3;
1213
-
1214
- position[anchor] = 0;
1215
- position[anchor + 1] = 0;
1216
- position[anchor + 2] = 0;
1217
-
1218
- velocity[anchor] = 0;
1219
- velocity[anchor + 1] = 0;
1220
- velocity[anchor + 2] = 0;
1771
+ function carry_with_holder(instance, h) {
1772
+ const hold = instance.holds[h];
1773
+
1774
+ const now = hold.pose;
1775
+ const before = hold.pose_previous;
1776
+
1777
+ // the jump's rotation, q_now ⊗ q_before⁻¹
1778
+ quat3_multiply_conjugate_right_array(JUMP, 0, now, 3, before, 3);
1779
+
1780
+ const state = instance.state;
1781
+ const position = state.position;
1782
+ const velocity = state.velocity;
1783
+
1784
+ const node_hold = instance.node_hold;
1785
+ const source = instance.tether_source;
1786
+ const particle_of_node = instance.particle_of_node;
1787
+
1788
+ for (let node = 0; node < instance.node_count; node++) {
1789
+ const owner = node_hold[node] !== -1 ? node_hold[node] : (source[node] === -1 ? -1 : node_hold[source[node]]);
1790
+
1791
+ if (owner !== h) {
1792
+ continue;
1793
+ }
1794
+
1795
+ const p = particle_of_node[node] * 3;
1796
+
1797
+ CARRIED[0] = position[p] - before[0];
1798
+ CARRIED[1] = position[p + 1] - before[1];
1799
+ CARRIED[2] = position[p + 2] - before[2];
1800
+
1801
+ pose3_apply_point_array(position, p, now, 0, JUMP, 0, CARRIED, 0);
1802
+
1803
+ v3_quaternion_apply_array(velocity, p, velocity, p, JUMP, 0);
1804
+ }
1221
1805
  }
1222
1806
 
1807
+ /**
1808
+ * @type {Float64Array}
1809
+ * @private
1810
+ */
1811
+ const JUMP = new Float64Array(4);
1812
+
1223
1813
  /**
1224
1814
  * @type {Float64Array}
1225
1815
  * @private
@@ -1285,46 +1875,3 @@ function cloth_inside_sleep_window(instance) {
1285
1875
  return true;
1286
1876
  }
1287
1877
 
1288
- /**
1289
- * Point every leash row at where it measures from: a slack row at the
1290
- * particle's animated position, a tether at the anchor.
1291
- *
1292
- * @param {ClothInstance} instance
1293
- * @private
1294
- */
1295
- function refresh_leash_targets(instance) {
1296
- const state = instance.state;
1297
-
1298
- const target = state.leash_target;
1299
-
1300
- const animated = instance.animated_position;
1301
-
1302
- const anchor = instance.particle_of_node[0] * 3;
1303
-
1304
- const anchor_x = state.position[anchor];
1305
- const anchor_y = state.position[anchor + 1];
1306
- const anchor_z = state.position[anchor + 2];
1307
-
1308
- const node_count = instance.node_count;
1309
-
1310
- const slack_row = instance.slack_row;
1311
- const tether_row = instance.tether_row;
1312
-
1313
- for (let node = 1; node < node_count; node++) {
1314
- const tether = tether_row[node];
1315
-
1316
- if (tether >= 0) {
1317
- target[tether * 3] = anchor_x;
1318
- target[tether * 3 + 1] = anchor_y;
1319
- target[tether * 3 + 2] = anchor_z;
1320
- }
1321
-
1322
- const slack = slack_row[node];
1323
-
1324
- if (slack >= 0) {
1325
- target[slack * 3] = animated[node * 3];
1326
- target[slack * 3 + 1] = animated[node * 3 + 1];
1327
- target[slack * 3 + 2] = animated[node * 3 + 2];
1328
- }
1329
- }
1330
- }