@woosh/meep-engine 3.24.0 → 3.26.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 (349) hide show
  1. package/package.json +1 -1
  2. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts +59 -0
  3. package/src/core/geom/3d/shape/HeightMapShape3D.d.ts.map +1 -1
  4. package/src/core/geom/3d/shape/HeightMapShape3D.js +285 -0
  5. package/src/core/geom/3d/shape/PosedShape3D.d.ts.map +1 -1
  6. package/src/core/geom/3d/shape/PosedShape3D.js +57 -0
  7. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  8. package/src/core/geom/3d/shape/TransformedShape3D.js +102 -0
  9. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  10. package/src/core/geom/3d/shape/UnionShape3D.js +64 -0
  11. package/src/engine/graphics3/PickingSystem.d.ts +3 -3
  12. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
  13. package/src/engine/graphics3/PickingSystem.js +52 -55
  14. package/src/engine/graphics3/shader_picking_gather.d.ts +40 -0
  15. package/src/engine/graphics3/shader_picking_gather.d.ts.map +1 -0
  16. package/src/engine/graphics3/shader_picking_gather.js +120 -0
  17. package/src/engine/physics/cloth/MEASUREMENTS.md +1632 -0
  18. package/src/engine/physics/cloth/PLAN.md +738 -95
  19. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +84 -17
  20. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -1
  21. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +451 -42
  22. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts +52 -0
  23. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts.map +1 -0
  24. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.js +70 -0
  25. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts +12 -3
  26. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts.map +1 -1
  27. package/src/engine/physics/cloth/ecs/ClothDynamics.js +340 -331
  28. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +101 -29
  29. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  30. package/src/engine/physics/cloth/ecs/ClothInstance.js +110 -31
  31. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts +84 -0
  32. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts.map +1 -0
  33. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.js +141 -0
  34. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +71 -81
  35. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  36. package/src/engine/physics/cloth/ecs/ClothSystem.js +135 -856
  37. package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts +48 -0
  38. package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts.map +1 -0
  39. package/src/engine/physics/cloth/ecs/ClothWorkerCore.js +107 -0
  40. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +323 -0
  41. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -0
  42. package/src/engine/physics/cloth/ecs/ClothWorld.js +1248 -0
  43. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +192 -0
  44. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -0
  45. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +578 -0
  46. package/src/engine/physics/cloth/ecs/cloth.worker.d.ts +2 -0
  47. package/src/engine/physics/cloth/ecs/cloth.worker.d.ts.map +1 -0
  48. package/src/engine/physics/cloth/ecs/cloth.worker.js +12 -0
  49. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  50. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +2 -1
  51. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +85 -3
  52. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
  53. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +366 -271
  54. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +95 -12
  55. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  56. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +170 -27
  57. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +19 -0
  58. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -1
  59. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +249 -201
  60. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +21 -0
  61. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -1
  62. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +62 -1
  63. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts +29 -0
  64. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts.map +1 -0
  65. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.js +78 -0
  66. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -1
  67. package/src/engine/physics/cloth/ecs/cloth_write_back.js +18 -10
  68. package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts +17 -0
  69. package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts.map +1 -0
  70. package/src/engine/physics/cloth/ecs/makeClothWorker.js +18 -0
  71. package/src/engine/physics/cloth/playground/README.md +73 -2
  72. package/src/engine/physics/cloth/playground/collide_main.js +1 -1
  73. package/src/engine/physics/cloth/playground/wind.html +166 -0
  74. package/src/engine/physics/cloth/playground/wind_build.d.ts +81 -0
  75. package/src/engine/physics/cloth/playground/wind_build.d.ts.map +1 -0
  76. package/src/engine/physics/cloth/playground/wind_build.js +141 -0
  77. package/src/engine/physics/cloth/playground/wind_main.d.ts +2 -0
  78. package/src/engine/physics/cloth/playground/wind_main.d.ts.map +1 -0
  79. package/src/engine/physics/cloth/playground/wind_main.js +681 -0
  80. package/src/engine/physics/cloth/solver/ClothState.d.ts +134 -79
  81. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  82. package/src/engine/physics/cloth/solver/ClothState.js +273 -12
  83. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +29 -8
  84. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -1
  85. package/src/engine/physics/cloth/solver/cloth_contact_find.js +37 -9
  86. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +45 -2
  87. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  88. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +46 -2
  89. package/src/engine/physics/cloth/solver/cloth_step.d.ts +21 -2
  90. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  91. package/src/engine/physics/cloth/solver/cloth_step.js +173 -4
  92. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +40 -0
  93. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -0
  94. package/src/engine/physics/cloth/solver/cloth_step_instance.js +284 -0
  95. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts +107 -0
  96. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts.map +1 -0
  97. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.js +263 -0
  98. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +9 -2
  99. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -1
  100. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +9 -2
  101. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +102 -0
  102. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -0
  103. package/src/engine/physics/cloth/wind/AbstractClothWind.js +114 -0
  104. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +87 -0
  105. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -0
  106. package/src/engine/physics/cloth/wind/ClothAmbientWind.js +185 -0
  107. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts +79 -0
  108. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -0
  109. package/src/engine/physics/cloth/wind/ClothFluidWind.js +213 -0
  110. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +106 -0
  111. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -0
  112. package/src/engine/physics/cloth/wind/cloth_sample_air.js +297 -0
  113. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  114. package/src/shade/device/mock/texture_copy.d.ts.map +1 -1
  115. package/src/shade/device/mock/texture_copy.js +28 -0
  116. package/src/shade/playground/vgeo_runtime/README.md +94 -0
  117. package/src/shade/playground/vgeo_runtime/index.html +62 -0
  118. package/src/shade/playground/vgeo_runtime/main.d.ts +2 -0
  119. package/src/shade/playground/vgeo_runtime/main.d.ts.map +1 -0
  120. package/src/shade/playground/vgeo_runtime/main.js +399 -0
  121. package/src/shade/playground/vgeo_viewer/sample_asset.js +1 -1
  122. package/src/shade/renderer/camera/Camera.d.ts +14 -0
  123. package/src/shade/renderer/camera/Camera.d.ts.map +1 -1
  124. package/src/shade/renderer/camera/Camera.js +7 -1
  125. package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.d.ts.map +1 -1
  126. package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.js +16 -1
  127. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  128. package/src/shade/renderer/geometry/GPUGeometryManager.js +29 -9
  129. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts +10 -0
  130. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts.map +1 -1
  131. package/src/shade/renderer/geometry/GPUGeometryMetadata.js +13 -0
  132. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +46 -18
  133. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +70 -22
  134. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +97 -19
  135. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +12 -2
  136. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  137. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +14 -3
  138. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +2 -2
  139. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +124 -6
  141. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +22 -0
  142. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +1 -0
  143. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.js +53 -0
  144. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +8 -1
  145. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  146. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +8 -1
  147. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts +16 -0
  148. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts.map +1 -0
  149. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.js +15 -0
  150. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts +54 -0
  151. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts.map +1 -0
  152. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.js +53 -0
  153. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +13 -2
  154. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  155. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +14 -2
  156. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +100 -0
  157. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  158. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +113 -0
  159. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
  160. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +5 -0
  161. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  162. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +108 -0
  163. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  164. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +23 -0
  165. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  166. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +40 -0
  167. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +228 -0
  168. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -0
  169. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +932 -0
  170. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +176 -0
  171. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -0
  172. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +425 -0
  173. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +241 -0
  174. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +574 -0
  175. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +120 -0
  176. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -0
  177. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +674 -0
  178. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +287 -0
  179. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -0
  180. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +976 -0
  181. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +42 -0
  182. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -0
  183. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +96 -0
  184. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts +16 -0
  185. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts.map +1 -0
  186. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.js +15 -0
  187. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts +19 -0
  188. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -0
  189. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +83 -0
  190. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts +9 -0
  191. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts.map +1 -0
  192. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.js +8 -0
  193. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +19 -0
  194. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -0
  195. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +160 -0
  196. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts +3 -0
  197. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -0
  198. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +255 -0
  199. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts +3 -0
  200. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -0
  201. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +80 -0
  202. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts +3 -0
  203. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -0
  204. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +144 -0
  205. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts +3 -0
  206. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -0
  207. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +530 -0
  208. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +39 -0
  209. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -0
  210. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +54 -0
  211. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts +41 -0
  212. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts.map +1 -0
  213. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.js +56 -0
  214. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts +49 -0
  215. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -0
  216. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +328 -0
  217. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +10 -0
  218. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts.map +1 -0
  219. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +51 -0
  220. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +13 -0
  221. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -0
  222. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +12 -0
  223. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +17 -0
  224. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts.map +1 -0
  225. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +99 -0
  226. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts +9 -0
  227. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts.map +1 -0
  228. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +8 -0
  229. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +7 -0
  230. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts.map +1 -0
  231. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +27 -0
  232. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts +14 -0
  233. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts.map +1 -0
  234. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.js +13 -0
  235. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts +7 -0
  236. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts.map +1 -0
  237. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.js +35 -0
  238. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts +9 -0
  239. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts.map +1 -0
  240. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.js +8 -0
  241. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts +15 -0
  242. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts.map +1 -0
  243. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.js +14 -0
  244. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +7 -0
  245. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts.map +1 -0
  246. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +36 -0
  247. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +10 -0
  248. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -0
  249. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +9 -0
  250. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts +11 -0
  251. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts.map +1 -0
  252. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.js +54 -0
  253. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts +9 -0
  254. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts.map +1 -0
  255. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.js +8 -0
  256. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts +11 -0
  257. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts.map +1 -0
  258. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.js +10 -0
  259. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts +7 -0
  260. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts.map +1 -0
  261. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.js +30 -0
  262. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts +9 -0
  263. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts.map +1 -0
  264. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.js +8 -0
  265. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts +13 -0
  266. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts.map +1 -0
  267. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.js +12 -0
  268. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts +14 -0
  269. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts.map +1 -0
  270. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.js +13 -0
  271. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +11 -0
  272. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts.map +1 -0
  273. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +61 -0
  274. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts +9 -0
  275. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -0
  276. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +8 -0
  277. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts +13 -0
  278. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts.map +1 -0
  279. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.js +12 -0
  280. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts +9 -0
  281. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts.map +1 -0
  282. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.js +8 -0
  283. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts +20 -0
  284. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts.map +1 -0
  285. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.js +19 -0
  286. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts +23 -0
  287. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -0
  288. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +70 -0
  289. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +18 -0
  290. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -0
  291. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +25 -0
  292. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +35 -0
  293. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -0
  294. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +41 -0
  295. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +32 -0
  296. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -0
  297. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +138 -0
  298. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  299. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  300. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  301. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +9 -0
  302. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  303. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +12 -1
  304. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  305. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +12 -1
  306. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  307. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +10 -0
  308. package/src/shade/renderer/scene/GPUSceneContext.d.ts +18 -0
  309. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  310. package/src/shade/renderer/scene/GPUSceneContext.js +647 -594
  311. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  312. package/src/shade/renderer/scene/rows/GPUSceneRows.js +22 -0
  313. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts +35 -0
  314. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts.map +1 -0
  315. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.js +91 -0
  316. package/src/engine/graphics3/instantiate_scene_bundle.d.ts +0 -54
  317. package/src/engine/graphics3/instantiate_scene_bundle.d.ts.map +0 -1
  318. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +0 -39
  319. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts.map +0 -1
  320. package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts +0 -35
  321. package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts.map +0 -1
  322. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts +0 -96
  323. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts.map +0 -1
  324. package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts +0 -20
  325. package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts.map +0 -1
  326. package/src/shade/renderer/animation/skin_bind_transform.d.ts +0 -25
  327. package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +0 -1
  328. package/src/shade/renderer/animation/skin_frame_defect.d.ts +0 -34
  329. package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +0 -1
  330. package/src/shade/renderer/animation/skin_joint_matrix.d.ts +0 -24
  331. package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +0 -1
  332. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +0 -70
  333. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +0 -1
  334. package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts +0 -96
  335. package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts.map +0 -1
  336. package/src/shade/renderer/scene/InstanceBatch.d.ts +0 -92
  337. package/src/shade/renderer/scene/InstanceBatch.d.ts.map +0 -1
  338. package/src/shade/renderer/scene/Mesh.d.ts +0 -55
  339. package/src/shade/renderer/scene/Mesh.d.ts.map +0 -1
  340. package/src/shade/renderer/scene/Node3D.d.ts +0 -159
  341. package/src/shade/renderer/scene/Node3D.d.ts.map +0 -1
  342. package/src/shade/renderer/scene/Node3DDirtyList.d.ts +0 -59
  343. package/src/shade/renderer/scene/Node3DDirtyList.d.ts.map +0 -1
  344. package/src/shade/renderer/scene/SceneNodeBinding.d.ts +0 -52
  345. package/src/shade/renderer/scene/SceneNodeBinding.d.ts.map +0 -1
  346. package/src/shade/renderer/scene/SkinnedMesh.d.ts +0 -27
  347. package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +0 -1
  348. package/src/shade/renderer/scene/TransformAuthority.d.ts +0 -19
  349. package/src/shade/renderer/scene/TransformAuthority.d.ts.map +0 -1
@@ -1,72 +1,30 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { quat3_multiply } from "../../../../core/geom/3d/quaternion/quat3_multiply.js";
3
- import { quat3_nlerp } from "../../../../core/geom/3d/quaternion/quat3_nlerp.js";
4
- import { v3_quaternion_apply } from "../../../../core/geom/vec3/v3_quaternion_apply.js";
5
- import { v3_quaternion_apply_inverse } from "../../../../core/geom/vec3/v3_quaternion_apply_inverse.js";
6
1
  import { ResourceAccessKind } from "../../../../core/model/ResourceAccessKind.js";
7
2
  import { ResourceAccessSpecification } from "../../../../core/model/ResourceAccessSpecification.js";
8
3
  import { System } from "../../../ecs/System.js";
9
4
  import {
10
5
  TransformAttachment
11
6
  } from "../../../ecs/transform-attachment/TransformAttachment.js";
12
- import {
13
- TransformAttachmentSystem
14
- } from "../../../ecs/transform-attachment/TransformAttachmentSystem.js";
15
- import {
16
- TRANSFORM64_ROTATION_OFFSET,
17
- TRANSFORM64_TRANSLATION_OFFSET,
18
- Transform64
19
- } from "../../../ecs/transform/Transform64.js";
20
- import {
21
- collect_entity_playbacks
22
- } from "../../../graphics3/pose/collect_entity_playbacks.js";
23
- import { MeshSystem } from "../../../graphics3/MeshSystem.js";
24
- import { t64_evaluate_world } from "../../../ecs/transform/t64_evaluate_world.js";
25
- import { cloth_contact_find } from "../solver/cloth_contact_find.js";
26
- import { cloth_self_find } from "../solver/cloth_self_find.js";
27
- import { CLOTH_SELF_RADIUS_MAX_RATIO, CLOTH_SELF_RADIUS_MIN_RATIO } from "../solver/cloth_solver_constants.js";
28
- import { cloth_step } from "../solver/cloth_step.js";
7
+ import { Transform64 } from "../../../ecs/transform/Transform64.js";
8
+ import { CLOTH_STEP_PARAM_STRIDE, cloth_step_instance } from "../solver/cloth_step_instance.js";
29
9
  import { Cloth } from "./Cloth.js";
30
10
  import { ClothCollider } from "./ClothCollider.js";
31
- import { ClothColliderSystem } from "./ClothColliderSystem.js";
32
11
  import { ClothExclude } from "./ClothExclude.js";
33
- import { ClothDynamicsFlags } from "./ClothDynamicsFlags.js";
34
- import { ClothFlags } from "./ClothFlags.js";
35
- import { ClothInstance } from "./ClothInstance.js";
36
12
  import { ClothRig } from "./ClothRig.js";
37
- import {
38
- CLOTH_ANCHOR_ROTATION_LIMIT,
39
- CLOTH_ANCHOR_TRAVEL_REACH,
40
- CLOTH_SLEEP_SPEED_RATIO,
41
- CLOTH_SLEEP_STEPS,
42
- CLOTH_STABILISE_STEPS,
43
- CLOTH_TELEPORT_FLOOR,
44
- CLOTH_TELEPORT_REACH,
45
- cloth_velocity_limit,
46
- cloth_velocity_retention
47
- } from "./cloth_dynamics_map.js";
48
- import {
49
- cloth_colliders_moved,
50
- cloth_gather_colliders
51
- } from "./cloth_gather_colliders.js";
52
- import {
53
- cloth_dynamics_change_needs_reseed,
54
- cloth_reparameterise
55
- } from "./cloth_build_rows.js";
56
- import { cloth_seed_subtree } from "./cloth_seed_subtree.js";
57
- import { cloth_seed_rig } from "./cloth_seed_rig.js";
58
- import { cloth_write_back } from "./cloth_write_back.js";
59
- import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
13
+ import { ClothWorld } from "./ClothWorld.js";
60
14
 
61
15
  /**
62
- * # The system — gather, step, write back
16
+ * # The system — gather, step, write back, all on this thread
17
+ *
18
+ * Every cloth in the scene stepped inline at the fixed rate. The work itself
19
+ * lives in {@link ClothWorld}, which this shares with {@link WorkerClothSystem}
20
+ * so that §12's "`ClothSystem` and `WorkerClothSystem` must produce bit-identical
21
+ * results" is a property of there being one copy rather than of two copies
22
+ * agreeing. What is left here is a `System`'s own surface — what it depends on,
23
+ * what it accesses, and the loop.
63
24
  *
64
- * One {@link ClothInstance} per {@link Cloth} component, stepped at the fixed
65
- * rate. Per step, per cloth: move the local frame with the anchor, point the
66
- * leash rows at where they measure from, step, and put the result back on the
67
- * subtree. The animated pose the leashes are measured against is not recomposed
68
- * per step — in the entity path it is constant in the anchor's own frame, and
69
- * `cloth_seed_subtree.js` bakes it.
25
+ * This is the fallback where `SharedArrayBuffer` is unavailable, and it is **not
26
+ * a degraded mode**: it is the same solver over the same data in the same
27
+ * order, and the only thing the worker buys is which thread pays for it.
70
28
  *
71
29
  * ## Where in the frame this runs
72
30
  *
@@ -97,29 +55,6 @@ import { cloth_write_back_rig } from "./cloth_write_back_rig.js";
97
55
  * it before `TransformAttachmentSystem`, which is right: cloth writes the local
98
56
  * transforms and the attachment system is their reader.
99
57
  *
100
- * ## The local frame and the inertia model — §7 P7
101
- *
102
- * Particles live in the anchor's own frame. When the anchor moves, the cloth is
103
- * asked how much of that motion it would like to feel:
104
- * `ClothDynamics#inertia = 1` keeps every particle's **world** position and
105
- * velocity, so a cape trails a sprinting character; `0` keeps their **local**
106
- * ones, so the cloth rides the anchor as though it were standing still. The
107
- * blend between the two is a lerp in the local frame, which is all the frame
108
- * change is.
109
- *
110
- * The felt motion is limited before the blend, which is what makes "character
111
- * sprints, cape behaves" a default rather than a tuning exercise, and the limits
112
- * are derived from the cloth's own reach rather than authored.
113
- *
114
- * ## Teleport — §7 P6
115
- *
116
- * Anchor motion past a threshold is not locomotion and is not treated as any
117
- * amount of it. `ClothFlags.TeleportKeep` carries the shape across, which in a
118
- * local frame is literally doing nothing; the default re-seeds at the animated
119
- * pose with zero velocity and opens a stabilisation window in which the
120
- * velocity limit ramps back from nothing, so the first steps after a reset
121
- * cannot produce a whip.
122
- *
123
58
  * @author Alex Goldring
124
59
  * @copyright Company Named Limited (c) 2026
125
60
  */
@@ -137,83 +72,111 @@ export class ClothSystem extends System {
137
72
  ];
138
73
 
139
74
  /**
140
- * World gravity, m/s². Rotated into each cloth's local frame per step.
75
+ * @type {ClothWorld}
76
+ */
77
+ world = new ClothWorld();
78
+
79
+ /**
80
+ * Every nominated cloth's step parameters, one block each, grown as the
81
+ * scene grows.
82
+ *
83
+ * The same blocks {@link WorkerClothSystem} writes into the shared command
84
+ * region — one layout, written by one function, so the inline path and the
85
+ * worker path cannot be handed different numbers. **One block per cloth
86
+ * rather than one reused block**, because the three phases of a tick are now
87
+ * three loops and a cloth's parameters have to survive from its `advance` to
88
+ * its step. See {@link fixedUpdate}.
141
89
  * @type {Float64Array}
90
+ * @private
142
91
  */
143
- gravity = new Float64Array([0, -9.81, 0]);
92
+ __params = new Float64Array(CLOTH_STEP_PARAM_STRIDE * 8);
144
93
 
145
94
  /**
146
- * Live cloths, ordered so that an instance whose anchor sits inside another
147
- * instance's subtree steps after it.
95
+ * The cloths `advance` nominated this tick, in the order it nominated them.
96
+ * Cleared at the top of every {@link fixedUpdate}; nothing here outlives a
97
+ * tick.
148
98
  * @type {ClothInstance[]}
99
+ * @private
149
100
  */
150
- instances = [];
101
+ __dispatched = [];
151
102
 
152
103
  /**
153
- * @type {Map<number, ClothInstance>}
154
- * @private
104
+ * World gravity, m/s². Rotated into each cloth's local frame per step.
105
+ * @returns {Float64Array}
155
106
  */
156
- __by_entity = new Map();
107
+ get gravity() {
108
+ return this.world.gravity;
109
+ }
157
110
 
158
111
  /**
159
- * @type {TransformAttachmentSystem|null}
160
- * @private
112
+ * @param {Float64Array} value
161
113
  */
162
- __attachments = null;
114
+ set gravity(value) {
115
+ this.world.gravity = value;
116
+ }
163
117
 
164
118
  /**
165
- * Where a skinned model instance comes from. Anything that answers
166
- * `instance_of(entity)` with a `PrefabInstance` — skins whose joints
167
- * are entities — will do, and {@link MeshSystem} is what does in a
168
- * running engine.
169
- *
170
- * Public and assignable because a garment's joints are a *rendering*
171
- * structure and `MeshSystem` needs a graphics engine, a scene and a loader
172
- * to exist — so a spec that wants joints without a renderer sets this
173
- * instead, and gets exactly the interface the system uses.
174
- *
175
- * `null` means no rig can seed, which is the same additive shape the
176
- * collider index has: a scene without one gets M1's cloth rather than an
177
- * error.
178
- * @type {{instance_of: function(number): (PrefabInstance|null)}|null}
119
+ * Where the air comes from — see {@link ClothWorld#wind}. `null` by default,
120
+ * which is a scene with no wind in it.
121
+ * @returns {AbstractClothWind|null}
179
122
  */
180
- models = null;
123
+ get wind() {
124
+ return this.world.wind;
125
+ }
181
126
 
182
127
  /**
183
- * The collider index's owner, or `null` when the manager has none — in
184
- * which case cloth simply does not collide, which is exactly M1's
185
- * behaviour and is what makes the collision layer additive rather than a
186
- * new requirement on every scene.
187
- * @type {ClothColliderSystem|null}
188
- * @private
128
+ * @param {AbstractClothWind|null} value
189
129
  */
190
- __colliders = null;
130
+ set wind(value) {
131
+ this.world.wind = value;
132
+ }
191
133
 
192
134
  /**
193
- * @type {boolean}
194
- * @private
135
+ * Live cloths, ordered so that an instance whose anchor sits inside another
136
+ * instance's subtree steps after it.
137
+ * @returns {ClothInstance[]}
138
+ */
139
+ get instances() {
140
+ return this.world.instances;
141
+ }
142
+
143
+ /**
144
+ * Where a skinned model instance comes from — see {@link ClothWorld#models}.
145
+ * @returns {{instance_of: function(number): (PrefabInstance|null)}|null}
195
146
  */
196
- __order_dirty = false;
147
+ get models() {
148
+ return this.world.models;
149
+ }
150
+
151
+ /**
152
+ * @param {{instance_of: function(number): (PrefabInstance|null)}|null} value
153
+ */
154
+ set models(value) {
155
+ this.world.models = value;
156
+ }
197
157
 
198
158
  /**
199
159
  * @param {EntityManager} entityManager
200
160
  * @returns {Promise<void>}
201
161
  */
202
162
  async startup(entityManager) {
203
- this.__attachments = entityManager.getSystem(TransformAttachmentSystem);
204
-
205
- assert.notNull(
206
- this.__attachments,
207
- 'ClothSystem needs a TransformAttachmentSystem: the subtree walk is its parent-to-children index'
208
- );
163
+ this.world.attach(entityManager);
164
+ }
209
165
 
210
- // Optional by design. A scene with no ClothColliderSystem gets cloth
211
- // that does not collide, rather than cloth that throws.
212
- this.__colliders = entityManager.getSystem(ClothColliderSystem) ?? null;
166
+ /**
167
+ * @param {EntityComponentDataset} dataset
168
+ * @returns {void}
169
+ */
170
+ handleDatasetAttached(dataset) {
171
+ this.world.attach_dataset(dataset);
172
+ }
213
173
 
214
- if (this.models === null) {
215
- this.models = entityManager.getSystem(MeshSystem) ?? null;
216
- }
174
+ /**
175
+ * @param {EntityComponentDataset} dataset
176
+ * @returns {void}
177
+ */
178
+ handleDatasetDetached(dataset) {
179
+ this.world.detach_dataset(dataset);
217
180
  }
218
181
 
219
182
  /**
@@ -222,16 +185,7 @@ export class ClothSystem extends System {
222
185
  * @param {number} entity
223
186
  */
224
187
  link(cloth, transform, entity) {
225
- const instance = new ClothInstance();
226
-
227
- instance.entity = entity;
228
- instance.cloth = cloth;
229
- instance.transform = transform;
230
-
231
- this.instances.push(instance);
232
- this.__by_entity.set(entity, instance);
233
-
234
- this.__order_dirty = true;
188
+ this.world.link(cloth, transform, entity);
235
189
  }
236
190
 
237
191
  /**
@@ -240,61 +194,25 @@ export class ClothSystem extends System {
240
194
  * @param {number} entity
241
195
  */
242
196
  unlink(cloth, transform, entity) {
243
- const instance = this.__by_entity.get(entity);
244
-
245
- if (instance === undefined) {
246
- return;
247
- }
248
-
249
- this.__by_entity.delete(entity);
250
-
251
- const index = this.instances.indexOf(instance);
252
-
253
- if (index >= 0) {
254
- this.instances.splice(index, 1);
255
- }
197
+ this.world.unlink(entity);
256
198
  }
257
199
 
258
200
  /**
259
201
  * Throw away a cloth's binding so the next step walks its subtree again.
260
- *
261
- * The walk is done once, because a rope's links do not come and go. When
262
- * they do — a chain gains a link, a rig is rebuilt — this is how you say so,
263
- * and it is the same contract `TransformAttachment#parent` already carries:
264
- * the structure is immutable while it is attached, and changing it is an
265
- * explicit act rather than something the engine polls for.
266
- *
267
202
  * @param {number} entity
268
203
  * @returns {void}
269
204
  */
270
205
  reseed(entity) {
271
- const instance = this.__by_entity.get(entity);
272
-
273
- if (instance === undefined) {
274
- return;
275
- }
276
-
277
- instance.seeded = false;
278
- instance.anchor_valid = false;
279
-
280
- this.__order_dirty = true;
206
+ this.world.reseed(entity);
281
207
  }
282
208
 
283
209
  /**
284
- * Wake a sleeping cloth. Anchor motion and a `ClothDynamics` change do this
285
- * on their own; a caller that has changed the world in some way the cloth
286
- * cannot see — M6's wind, for one — says so here.
287
- *
210
+ * Wake a sleeping cloth.
288
211
  * @param {number} entity
289
212
  * @returns {void}
290
213
  */
291
214
  wake(entity) {
292
- const instance = this.__by_entity.get(entity);
293
-
294
- if (instance !== undefined) {
295
- instance.asleep = false;
296
- instance.still_steps = 0;
297
- }
215
+ this.world.wake(entity);
298
216
  }
299
217
 
300
218
  /**
@@ -302,10 +220,29 @@ export class ClothSystem extends System {
302
220
  * @returns {ClothInstance|undefined}
303
221
  */
304
222
  instanceOf(entity) {
305
- return this.__by_entity.get(entity);
223
+ return this.world.instanceOf(entity);
306
224
  }
307
225
 
308
226
  /**
227
+ * Advance every cloth, then step every cloth, then finish every cloth.
228
+ *
229
+ * **Three loops rather than one**, and the reason is §12's parity gate.
230
+ * `WorkerClothSystem` cannot interleave them: its `apply` writes step T−1
231
+ * back at the top of the tick and its `collect` then advances everything for
232
+ * step T, because the step in between happened on another thread. So a
233
+ * nested cloth there reads its parent's **previous** step's pose, and a
234
+ * nested cloth here used to read the pose the cloth above it had written
235
+ * *this* step — `ClothWorld` sorted the instances by hierarchy depth
236
+ * specifically so that it would. Measured on a 4-link rope hung from link 3
237
+ * of a 6-link rope, no wind, 120 steps: the parent identical, the child
238
+ * differing at **step 1, byte 24**.
239
+ *
240
+ * Both readings are defensible and only one of them can be the contract.
241
+ * This is the one that costs nothing to make true on both sides — and once
242
+ * it is, the depth sort does nothing, so it is gone. What a nested cloth
243
+ * reads is its parent one fixed step stale, which is the same trade §4.1
244
+ * already makes for the anchor and measures at a few centimetres.
245
+ *
309
246
  * @param {number} dt seconds
310
247
  */
311
248
  fixedUpdate(dt) {
@@ -315,706 +252,48 @@ export class ClothSystem extends System {
315
252
  return;
316
253
  }
317
254
 
318
- const instances = this.instances;
255
+ const world = this.world;
319
256
 
320
- if (this.__order_dirty) {
321
- this.#reorder(ecd);
322
- }
323
-
324
- // The index is refreshed here rather than in its own system's fixed
325
- // update, because the engine's ordering cannot put a reader after a
326
- // writer of the same component and `ClothColliderSystem` reads strictly
327
- // less than this system does. §4.1's conclusion, applied a second time:
328
- // the answer is not a third mechanism, it is one call site.
329
- if (this.__colliders !== null) {
330
- this.__colliders.refresh();
331
- }
332
-
333
- for (let i = 0; i < instances.length; i++) {
334
- const instance = instances[i];
335
-
336
- if (instance.refused) {
337
- continue;
338
- }
339
-
340
- if (!instance.seeded) {
341
- this.#seed(instance, ecd, dt);
342
-
343
- if (!instance.seeded) {
344
- // A rig whose model has not finished loading. Nothing is
345
- // wrong and nothing is retried-with-backoff: the next step
346
- // asks again, and a model that never loads costs one map
347
- // lookup a step.
348
- continue;
349
- }
350
- } else if (!instance.cloth.dynamics.equals(instance.dynamics_seen)) {
351
- if (cloth_dynamics_change_needs_reseed(instance.dynamics_seen, instance.cloth.dynamics)) {
352
- this.#seed(instance, ecd, dt);
353
- } else {
354
- cloth_reparameterise(instance, dt);
355
- }
356
-
357
- instance.asleep = false;
358
- instance.still_steps = 0;
359
- }
360
-
361
- this.#advance(instance, ecd, dt);
362
- }
363
- }
364
-
365
- /**
366
- * Walk whichever hierarchy this cloth is built on.
367
- *
368
- * **The discriminator is a component's presence** (§3.1): `Cloth` alone
369
- * simulates the entity's own transform subtree, `Cloth` + {@link ClothRig}
370
- * simulates the joints of the skinned model the entity is showing. There is
371
- * no mode field anywhere, and the solver does not know which ran.
372
- *
373
- * A rig can fail to seed for reasons that are not errors — the model has
374
- * not loaded, or nothing in the manager hands out model instances — and one
375
- * that does is left unseeded for the next step to try again. A rig that
376
- * fails for a reason that *is* an error says so once and then stops, because
377
- * a proxy that does not fit its skin will not start fitting it.
378
- *
379
- * @param {ClothInstance} instance
380
- * @param {EntityComponentDataset} ecd
381
- * @param {number} dt
382
- * @private
383
- */
384
- #seed(instance, ecd, dt) {
385
- const rig = ecd.getComponent(instance.entity, ClothRig);
386
-
387
- if (rig === undefined || rig === null || rig.proxy === null) {
388
- instance.rig = null;
389
-
390
- cloth_seed_subtree(instance, ecd, this.__attachments, dt);
391
-
392
- return;
393
- }
257
+ world.begin(ecd, dt);
394
258
 
395
- if (this.models === null) {
396
- this.#refuse(instance, `entity ${instance.entity} has a ClothRig and nothing hands out model instances`);
259
+ const instances = world.instances;
397
260
 
398
- return;
399
- }
261
+ const dispatched = this.__dispatched;
400
262
 
401
- const model = this.models.instance_of(instance.entity);
263
+ dispatched.length = 0;
402
264
 
403
- if (model === null || model === undefined) {
404
- // Still loading. Not an error, and not worth a message.
405
- return;
265
+ if (this.__params.length < instances.length * CLOTH_STEP_PARAM_STRIDE) {
266
+ this.__params = new Float64Array(instances.length * CLOTH_STEP_PARAM_STRIDE);
406
267
  }
407
268
 
408
- const skin = model.skins[rig.skin];
409
-
410
- const error = rig.proxy.checkAgainst(skin);
411
-
412
- if (error !== "") {
413
- this.#refuse(instance, `entity ${instance.entity}: ${error}`);
414
-
415
- return;
416
- }
417
-
418
- instance.rig = rig;
419
-
420
- cloth_seed_rig(instance, rig.proxy, skin.joints, ecd, dt);
421
- }
422
-
423
- /**
424
- * Say once why a rig will not seed, and stop asking.
425
- *
426
- * @param {ClothInstance} instance
427
- * @param {string} message
428
- * @private
429
- */
430
- #refuse(instance, message) {
431
- if (instance.refused) {
432
- return;
433
- }
434
-
435
- instance.refused = true;
436
-
437
- console.warn(`ClothSystem: ${message}`);
438
- }
439
-
440
- /**
441
- * Depth-first order by hierarchy depth, so a nested cloth reads an anchor
442
- * the cloth above it has already written.
443
- *
444
- * @param {EntityComponentDataset} ecd
445
- * @private
446
- */
447
- #reorder(ecd) {
448
- const instances = this.instances;
269
+ const params = this.__params;
449
270
 
450
271
  for (let i = 0; i < instances.length; i++) {
451
272
  const instance = instances[i];
452
273
 
453
- let depth = 0;
454
- let entity = instance.entity;
455
-
456
- // Bounded by the number of entities; a cycle in the attachment
457
- // graph is not this system's to diagnose, but it is this loop's to
458
- // survive.
459
- for (let guard = 0; guard < 4096; guard++) {
460
- const attachment = ecd.getComponent(entity, TransformAttachment);
461
-
462
- if (attachment === undefined) {
463
- break;
464
- }
465
-
466
- entity = attachment.parent;
467
- depth++;
274
+ if (!world.ensure_seeded(instance, ecd, dt)) {
275
+ continue;
468
276
  }
469
277
 
470
- instance.depth = depth;
471
- }
472
-
473
- instances.sort((a, b) => a.depth - b.depth);
474
-
475
- this.__order_dirty = false;
476
- }
477
-
478
- /**
479
- * @param {ClothInstance} instance
480
- * @param {EntityComponentDataset} ecd
481
- * @param {number} dt
482
- * @private
483
- */
484
- #advance(instance, ecd, dt) {
485
- const transform = instance.rig === null
486
- ? instance.transform
487
- : this.#anchor_pose(instance);
488
-
489
- const tx = transform[TRANSFORM64_TRANSLATION_OFFSET];
490
- const ty = transform[TRANSFORM64_TRANSLATION_OFFSET + 1];
491
- const tz = transform[TRANSFORM64_TRANSLATION_OFFSET + 2];
492
-
493
- const qx = transform[TRANSFORM64_ROTATION_OFFSET];
494
- const qy = transform[TRANSFORM64_ROTATION_OFFSET + 1];
495
- const qz = transform[TRANSFORM64_ROTATION_OFFSET + 2];
496
- const qw = transform[TRANSFORM64_ROTATION_OFFSET + 3];
497
-
498
- const previous_translation = instance.anchor_translation;
499
- const previous_rotation = instance.anchor_rotation;
500
-
501
- if (instance.cloth.blend !== instance.blend_seen) {
502
- instance.asleep = false;
503
- instance.still_steps = 0;
504
- }
505
-
506
- const moved = instance.anchor_valid && (
507
- previous_translation[0] !== tx
508
- || previous_translation[1] !== ty
509
- || previous_translation[2] !== tz
510
- || previous_rotation[0] !== qx
511
- || previous_rotation[1] !== qy
512
- || previous_rotation[2] !== qz
513
- || previous_rotation[3] !== qw
514
- );
515
-
516
- if (moved) {
517
- instance.asleep = false;
518
- instance.still_steps = 0;
519
- }
520
-
521
- if (instance.asleep && this.__colliders !== null && instance.collider_count >= 0) {
522
- // §7 P5's "wake on collider motion". A sleeping cloth is neither
523
- // stepped nor written back, so something has to notice the world it
524
- // was resting against moving out from under it, and a broadphase
525
- // query plus a pose comparison is three orders of magnitude cheaper
526
- // than the step it decides whether to skip.
527
- if (cloth_colliders_moved(instance, this.__colliders.index)) {
528
- instance.asleep = false;
529
- instance.still_steps = 0;
278
+ if (!world.advance(instance, dt, params, dispatched.length * CLOTH_STEP_PARAM_STRIDE)) {
279
+ continue;
530
280
  }
531
- }
532
281
 
533
- if (instance.asleep) {
534
- // Nothing is stepped and nothing is written: the pose on the
535
- // subtree is already the pose, and rewriting it is what would make
536
- // §7 P5's "bit-identical across the following 300 steps" an
537
- // aspiration rather than a fact.
538
- return;
282
+ dispatched.push(instance);
539
283
  }
540
284
 
541
- if (instance.anchor_valid) {
542
- const displacement = Math.hypot(tx - previous_translation[0], ty - previous_translation[1], tz - previous_translation[2]);
285
+ for (let i = 0; i < dispatched.length; i++) {
286
+ const instance = dispatched[i];
543
287
 
544
- const teleport = displacement > Math.max(
545
- CLOTH_TELEPORT_REACH * instance.reach,
546
- CLOTH_TELEPORT_FLOOR
288
+ cloth_step_instance(
289
+ instance.state, instance.self_exclusions, params, i * CLOTH_STEP_PARAM_STRIDE
547
290
  );
548
-
549
- if (teleport) {
550
- this.#teleport(instance);
551
- } else {
552
- move_local_frame(
553
- instance,
554
- tx, ty, tz, qx, qy, qz, qw,
555
- previous_translation, previous_rotation
556
- );
557
- }
558
291
  }
559
292
 
560
- previous_translation[0] = tx;
561
- previous_translation[1] = ty;
562
- previous_translation[2] = tz;
563
-
564
- previous_rotation[0] = qx;
565
- previous_rotation[1] = qy;
566
- previous_rotation[2] = qz;
567
- previous_rotation[3] = qw;
568
-
569
- instance.anchor_valid = true;
570
-
571
- refresh_leash_targets(instance);
572
-
573
- const dynamics = instance.cloth.dynamics;
574
-
575
- damp(instance, cloth_velocity_retention(dynamics.damping, dt));
576
-
577
- // Gravity is a world vector and the solve is in the anchor's frame.
578
- const gravity = this.gravity;
579
-
580
- v3_quaternion_apply_inverse(LOCAL_GRAVITY, 0, gravity[0], gravity[1], gravity[2], qx, qy, qz, qw);
581
-
582
- const state = instance.state;
583
-
584
- let limit = cloth_velocity_limit(
585
- instance.shortest_row, dynamics.substeps, dynamics.iterations, dt
586
- );
587
-
588
- state.contact_radius = dynamics.thickness;
589
-
590
- if (this.__colliders !== null) {
591
- // The gather's margin is how far a particle may travel this step,
592
- // which is the velocity clamp read as a distance — the quantity it
593
- // was built out of (`cloth_dynamics_map.js`).
594
- cloth_gather_colliders(instance, this.__colliders.index, limit * dt);
595
-
596
- instance.contact_count = cloth_contact_find(
597
- state, instance.collider_table, instance.collider_key, instance.collider_count
598
- );
599
- } else {
600
- instance.collider_count = 0;
601
- instance.contact_count = 0;
602
- }
603
-
604
- if ((dynamics.flags & ClothDynamicsFlags.SelfCollision) !== 0) {
605
- // Half the shortest row, so the row's diameter is one particle
606
- // spacing. `ClothState#self_radius` carries why the fabric's own
607
- // thickness is the wrong length here and `MEASUREMENTS.md` §31 is the
608
- // sweep that settled it. A fabric thicker than its proxy is coarse
609
- // keeps its thickness — and is then clamped under the closest pair
610
- // the proxy has with no row between them, because a diameter that
611
- // reaches that pair puts a cloth which is not folded at all in
612
- // permanent contact with itself (§36, §37).
613
- state.self_radius = Math.min(
614
- Math.max(dynamics.thickness, instance.shortest_row * CLOTH_SELF_RADIUS_MIN_RATIO),
615
- instance.closest_free_pair * CLOTH_SELF_RADIUS_MAX_RATIO
616
- );
617
-
618
- instance.self_pairs = cloth_self_find(state, instance.self_exclusions);
619
- } else {
620
- state.self_count = 0;
621
- instance.self_pairs = 0;
622
- }
623
-
624
- cloth_step(
625
- state, dt,
626
- LOCAL_GRAVITY[0], LOCAL_GRAVITY[1], LOCAL_GRAVITY[2],
627
- dynamics.substeps, dynamics.iterations,
628
- instance.collider_table, instance.collider_count
629
- );
630
-
631
- if (instance.stabilise_remaining > 0) {
632
- // Ramp back from nothing over the window. The first step after a
633
- // reset is allowed no velocity at all, which is what stops a
634
- // re-seeded cloth from snapping taut and whipping.
635
- limit *= (CLOTH_STABILISE_STEPS - instance.stabilise_remaining) / CLOTH_STABILISE_STEPS;
636
-
637
- instance.stabilise_remaining--;
638
- }
639
-
640
- const speed = clamp_velocity(instance, limit);
641
-
642
- if ((instance.cloth.flags & ClothFlags.NeverSleep) === 0) {
643
- if (speed < CLOTH_SLEEP_SPEED_RATIO * instance.reach) {
644
- instance.still_steps++;
645
-
646
- if (instance.still_steps >= CLOTH_SLEEP_STEPS) {
647
- instance.asleep = true;
648
- }
649
- } else {
650
- instance.still_steps = 0;
651
- }
652
- }
653
-
654
- instance.blend_seen = instance.cloth.blend;
655
-
656
- if (instance.rig === null) {
657
- cloth_write_back(instance, ecd);
658
- } else {
659
- cloth_write_back_rig(instance, ecd);
660
- }
661
- }
662
-
663
- /**
664
- * The anchor joint's world pose, computed on the CPU from the clips the
665
- * entity is playing.
666
- *
667
- * **Computed, never read back**, for two reasons that are both the engine's
668
- * own. A joint of an animated skeleton is the GPU's (`GPUStateAuthority`),
669
- * so nothing maintains its CPU-side world and reading it gives its rest
670
- * pose under whatever the CPU last composed. And GPU readback is a frame
671
- * late and asynchronous, so anything depending on it would depend on frame
672
- * pacing — which is why `query_entity_node_world_pose` exists and is
673
- * documented as the only sanctioned way to ask this question. A cape and a
674
- * scabbard on the same shoulder get their answer from the same evaluator,
675
- * off the same clock, which is what keeps them on the same shoulder.
676
- *
677
- * @param {ClothInstance} instance
678
- * @returns {Transform64}
679
- * @private
680
- */
681
- #anchor_pose(instance) {
682
- const playbacks = PLAYBACKS;
683
-
684
- playbacks.length = 0;
685
-
686
- collect_entity_playbacks(playbacks, this.entityManager, instance.entity);
687
-
688
- t64_evaluate_world(instance.anchor_pose, this.entityManager.dataset, instance.node_joint[0], playbacks);
689
-
690
- playbacks.length = 0;
691
-
692
- return instance.anchor_pose;
693
- }
694
-
695
- /**
696
- * @param {ClothInstance} instance
697
- * @private
698
- */
699
- #teleport(instance) {
700
- instance.teleport_count++;
701
-
702
- if ((instance.cloth.flags & ClothFlags.TeleportKeep) !== 0) {
703
- // `Keep` carries the cloth's shape across the jump. Particles are
704
- // stored relative to the anchor, so that is exactly what happens if
705
- // this function does nothing to them — the frame moved and they did
706
- // not, which is MagicaCloth2's rigid delta expressed in the frame
707
- // that makes it free.
708
- return;
709
- }
710
-
711
- const state = instance.state;
712
-
713
- const node_count = instance.node_count;
714
- const animated = instance.animated_position;
715
- const particle_of_node = instance.particle_of_node;
716
-
717
- for (let node = 0; node < node_count; node++) {
718
- const p = particle_of_node[node] * 3;
719
- const n = node * 3;
720
-
721
- state.position[p] = animated[n];
722
- state.position[p + 1] = animated[n + 1];
723
- state.position[p + 2] = animated[n + 2];
724
- }
725
-
726
- state.position_previous.set(state.position);
727
- state.velocity.fill(0);
728
- state.inertia_blend.fill(0);
729
-
730
- // The multipliers describe forces in a configuration that no longer
731
- // exists. Carrying them across would apply the old pose's tension to
732
- // the new one on the first iteration, which is a whip with extra steps.
733
- state.stretch_lambda.fill(0);
734
- state.bend_lambda.fill(0);
735
- state.leash_lambda.fill(0);
736
-
737
- instance.stabilise_remaining = CLOTH_STABILISE_STEPS;
738
- }
739
- }
740
-
741
- /**
742
- * The clip set handed to the pose evaluator, rebuilt per call from the
743
- * animation systems' own entries. Nothing in here outlives the call.
744
- * @type {PosePlayback[]}
745
- * @private
746
- */
747
- const PLAYBACKS = [];
748
-
749
- /**
750
- * @type {Float64Array}
751
- * @private
752
- */
753
- const LOCAL_GRAVITY = new Float64Array(3);
754
-
755
- /**
756
- * @type {Float64Array}
757
- * @private
758
- */
759
- const FRAME = new Float64Array(8);
760
-
761
- /**
762
- * Carry the particles across a change of anchor pose, by however much of it the
763
- * fabric is willing to feel. §7 P7.
764
- *
765
- * A particle at local `p` was at world `t_prev + q_prev·p`. Holding that world
766
- * position fixed, its local coordinates become `D + Q·p` with
767
- * `D = q_now⁻¹·(t_prev − t_now)` and `Q = q_now⁻¹ ⊗ q_prev`. Feeling *all* of
768
- * the anchor's motion is taking that; feeling *none* is leaving `p` alone; and
769
- * `inertia` is the lerp between them. Velocity is carried the same way, minus
770
- * the translation, since a translation of the frame does not rotate a vector.
771
- *
772
- * `D` and `Q` are limited before the lerp. The limits are the cloth's own reach
773
- * per step and a fixed angle per step, which is what makes an anchor that
774
- * teleports a metre — or spins — something a cape *lags behind* rather than
775
- * something it is fired out of.
776
- *
777
- * @param {ClothInstance} instance
778
- * @param {number} tx
779
- * @param {number} ty
780
- * @param {number} tz
781
- * @param {number} qx
782
- * @param {number} qy
783
- * @param {number} qz
784
- * @param {number} qw
785
- * @param {Float64Array} previous_translation
786
- * @param {Float64Array} previous_rotation
787
- * @private
788
- */
789
- function move_local_frame(
790
- instance,
791
- tx, ty, tz, qx, qy, qz, qw,
792
- previous_translation, previous_rotation
793
- ) {
794
- let inertia = instance.cloth.dynamics.inertia;
795
-
796
- if (inertia <= 0) {
797
- return;
798
- }
799
-
800
- if (inertia > 1) {
801
- inertia = 1;
802
- }
803
-
804
- v3_quaternion_apply_inverse(
805
- FRAME, 0,
806
- previous_translation[0] - tx,
807
- previous_translation[1] - ty,
808
- previous_translation[2] - tz,
809
- qx, qy, qz, qw
810
- );
811
-
812
- const travel_limit = CLOTH_ANCHOR_TRAVEL_REACH * instance.reach;
813
-
814
- const travel = Math.hypot(FRAME[0], FRAME[1], FRAME[2]);
815
-
816
- if (travel > travel_limit && travel > 0) {
817
- const scale = travel_limit / travel;
818
-
819
- FRAME[0] *= scale;
820
- FRAME[1] *= scale;
821
- FRAME[2] *= scale;
822
- }
823
-
824
- quat3_multiply(
825
- FRAME, 4,
826
- -qx, -qy, -qz, qw,
827
- previous_rotation[0], previous_rotation[1], previous_rotation[2], previous_rotation[3]
828
- );
829
-
830
- // `|w|` is `cos(θ/2)`, so this is the angle test without an `acos`.
831
- const half_angle_cos = FRAME[7] < 0 ? -FRAME[7] : FRAME[7];
832
-
833
- if (half_angle_cos < Math.cos(CLOTH_ANCHOR_ROTATION_LIMIT * 0.5)) {
834
- const angle = 2 * Math.acos(half_angle_cos > 1 ? 1 : half_angle_cos);
835
-
836
- quat3_nlerp(
837
- FRAME, 4,
838
- 0, 0, 0, 1,
839
- FRAME[4], FRAME[5], FRAME[6], FRAME[7],
840
- CLOTH_ANCHOR_ROTATION_LIMIT / angle
841
- );
842
- }
843
-
844
- const state = instance.state;
845
-
846
- const particle_count = state.particle_count;
847
-
848
- const position = state.position;
849
- const velocity = state.velocity;
850
-
851
- const dx = FRAME[0];
852
- const dy = FRAME[1];
853
- const dz = FRAME[2];
854
-
855
- const rx = FRAME[4];
856
- const ry = FRAME[5];
857
- const rz = FRAME[6];
858
- const rw = FRAME[7];
859
-
860
- for (let i = 0; i < particle_count; i++) {
861
- const p = i * 3;
862
-
863
- v3_quaternion_apply(CARRIED, 0, position[p], position[p + 1], position[p + 2], rx, ry, rz, rw);
864
-
865
- position[p] += inertia * (CARRIED[0] + dx - position[p]);
866
- position[p + 1] += inertia * (CARRIED[1] + dy - position[p + 1]);
867
- position[p + 2] += inertia * (CARRIED[2] + dz - position[p + 2]);
868
-
869
- v3_quaternion_apply(CARRIED, 0, velocity[p], velocity[p + 1], velocity[p + 2], rx, ry, rz, rw);
870
-
871
- velocity[p] += inertia * (CARRIED[0] - velocity[p]);
872
- velocity[p + 1] += inertia * (CARRIED[1] - velocity[p + 1]);
873
- velocity[p + 2] += inertia * (CARRIED[2] - velocity[p + 2]);
874
- }
875
-
876
- // The anchor is pinned and is the origin of the frame it anchors, so it is
877
- // the one particle that must not be carried anywhere.
878
- const anchor = instance.particle_of_node[0] * 3;
879
-
880
- position[anchor] = 0;
881
- position[anchor + 1] = 0;
882
- position[anchor + 2] = 0;
883
-
884
- velocity[anchor] = 0;
885
- velocity[anchor + 1] = 0;
886
- velocity[anchor + 2] = 0;
887
- }
888
-
889
- /**
890
- * @type {Float64Array}
891
- * @private
892
- */
893
- const CARRIED = new Float64Array(3);
894
-
895
- /**
896
- * Point every leash row at where it measures from: a slack row at the
897
- * particle's animated position, a tether at the anchor.
898
- *
899
- * @param {ClothInstance} instance
900
- * @private
901
- */
902
- function refresh_leash_targets(instance) {
903
- const state = instance.state;
904
-
905
- const target = state.leash_target;
906
-
907
- const animated = instance.animated_position;
908
-
909
- const anchor = instance.particle_of_node[0] * 3;
910
-
911
- const anchor_x = state.position[anchor];
912
- const anchor_y = state.position[anchor + 1];
913
- const anchor_z = state.position[anchor + 2];
914
-
915
- const node_count = instance.node_count;
916
-
917
- const slack_row = instance.slack_row;
918
- const tether_row = instance.tether_row;
919
-
920
- for (let node = 1; node < node_count; node++) {
921
- const tether = tether_row[node];
922
-
923
- if (tether >= 0) {
924
- target[tether * 3] = anchor_x;
925
- target[tether * 3 + 1] = anchor_y;
926
- target[tether * 3 + 2] = anchor_z;
293
+ for (let i = 0; i < dispatched.length; i++) {
294
+ world.finish(dispatched[i], ecd);
927
295
  }
928
296
 
929
- const slack = slack_row[node];
930
-
931
- if (slack >= 0) {
932
- target[slack * 3] = animated[node * 3];
933
- target[slack * 3 + 1] = animated[node * 3 + 1];
934
- target[slack * 3 + 2] = animated[node * 3 + 2];
935
- }
297
+ dispatched.length = 0;
936
298
  }
937
299
  }
938
-
939
- /**
940
- * Scale every particle's velocity, which is §7 P5's other half.
941
- *
942
- * The multiplier decay `α·γ` bleeds accumulated force out of a **hard** row,
943
- * and a soft row has no multiplier to decay — M0 measured a soft cloth landing
944
- * in a bounded limit cycle that 2,000 further steps do not move. This is what
945
- * reaches it, and it is why `ClothDynamics#damping` is part of the correctness
946
- * argument rather than a taste control. `MEASUREMENTS.md` §6.
947
- *
948
- * @param {ClothInstance} instance
949
- * @param {number} retention `[0, 1]`
950
- * @private
951
- */
952
- function damp(instance, retention) {
953
- if (retention >= 1) {
954
- return;
955
- }
956
-
957
- const velocity = instance.state.velocity;
958
-
959
- for (let i = 0; i < velocity.length; i++) {
960
- velocity[i] *= retention;
961
- }
962
- }
963
-
964
- /**
965
- * Hold every particle's speed under the limit derived from
966
- * `ClothDynamics#thickness`, and report the largest speed left afterwards so
967
- * the sleep test has something to read.
968
- *
969
- * §7 P1 names this clamp and M0 deliberately left it out, because a clamp would
970
- * have masked the property M0 existed to measure. It is here rather than inside
971
- * `cloth_step` for the same reason: the solver's stability is still its own.
972
- *
973
- * Speed rather than displacement is what the sleep test reads, because
974
- * `position_previous` after a multi-substep step holds the start of the *last*
975
- * substep rather than the start of the step, and a motion measure that quietly
976
- * divides by the substep count is a sleep threshold that means something
977
- * different for every fabric.
978
- *
979
- * @param {ClothInstance} instance
980
- * @param {number} limit m/s
981
- * @returns {number} largest particle speed after clamping, m/s
982
- * @private
983
- */
984
- function clamp_velocity(instance, limit) {
985
- const state = instance.state;
986
-
987
- const particle_count = state.particle_count;
988
-
989
- const velocity = state.velocity;
990
-
991
- const limit_squared = limit * limit;
992
-
993
- let worst_squared = 0;
994
-
995
- for (let i = 0; i < particle_count; i++) {
996
- const p = i * 3;
997
-
998
- const vx = velocity[p];
999
- const vy = velocity[p + 1];
1000
- const vz = velocity[p + 2];
1001
-
1002
- let speed_squared = vx * vx + vy * vy + vz * vz;
1003
-
1004
- if (speed_squared > limit_squared) {
1005
- const scale = limit / Math.sqrt(speed_squared);
1006
-
1007
- velocity[p] = vx * scale;
1008
- velocity[p + 1] = vy * scale;
1009
- velocity[p + 2] = vz * scale;
1010
-
1011
- speed_squared = limit_squared;
1012
- }
1013
-
1014
- if (speed_squared > worst_squared) {
1015
- worst_squared = speed_squared;
1016
- }
1017
- }
1018
-
1019
- return Math.sqrt(worst_squared);
1020
- }