@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,4 +1,8 @@
1
1
  import { assert } from "../../../../core/assert.js";
2
+ import { BVH, NULL_NODE } from "../../../../core/bvh2/bvh3/BVH.js";
3
+ import {
4
+ bvh_query_user_data_intersects_aabb
5
+ } from "../../../../core/bvh2/bvh3/query/bvh_query_user_data_intersects_aabb.js";
2
6
  import {
3
7
  CCR_AABB_MAX_X,
4
8
  CCR_AABB_MAX_Y,
@@ -12,6 +16,7 @@ import {
12
16
  CCR_META_LAYER,
13
17
  CCR_META_MASK,
14
18
  CCR_META_STRIDE,
19
+ CCR_KIND,
15
20
  CCR_PREV_QW,
16
21
  CCR_PREV_QX,
17
22
  CCR_PREV_QY,
@@ -30,6 +35,7 @@ import {
30
35
  } from "./ClothColliderRecord.js";
31
36
  import { cloth_collider_refit_bounds } from "./cloth_collider_bounds.js";
32
37
  import { cloth_collider_pack } from "./cloth_collider_pack.js";
38
+ import { ClothColliderKind } from "./ClothColliderKind.js";
33
39
 
34
40
  /**
35
41
  * # The index of live cloth colliders
@@ -41,23 +47,47 @@ import { cloth_collider_pack } from "./cloth_collider_pack.js";
41
47
  * deliberately **not** the physics BVH, because `ColliderObserverSystem` skips
42
48
  * colliders with no owning body and a character's bone colliders have none.
43
49
  *
44
- * ## Why a linear scan and not a BVH
50
+ * ## A tree, and the argument that used to stand here
51
+ *
52
+ * This was a linear scan, on two stated grounds that were both wrong.
53
+ *
54
+ * The first was that the set is "a character's bones — twenty or so, and never
55
+ * the whole world". That number came from `MEASUREMENTS.md` §41, which counted
56
+ * the colliders **overlapping one garment** in a six-character scene. That is a
57
+ * narrowphase input, and it was used to size a structure that holds every
58
+ * `ClothCollider` in the world. The index is queried once per cloth per step,
59
+ * so a scan is `clothes × colliders` per step in an engine that is built to
60
+ * carry far more of both.
45
61
  *
46
- * `AcousticOccluderIndex` puts a BVH over its bodies and this does not, for two
47
- * reasons that are both about what the query is.
62
+ * The second was that a tree over leaves that all move every frame costs more
63
+ * to keep than to skip. `PhysicsSystem` keeps two dynamic {@link BVH}s over
64
+ * bodies that all move every frame, and `compute_fat_world_aabb` is why it is
65
+ * cheap: a leaf stores a box fattened past the tight one, and is only refit
66
+ * when the tight box escapes it. {@link setPose} does the same, so a resting
67
+ * character pays a containment test per bone per step and no tree traffic at
68
+ * all.
48
69
  *
49
- * The query here is one AABB overlap per cloth per step against a set that is a
50
- * character's bones — twenty or so, and never the whole world, because a
51
- * `ClothCollider` is an opt-in marker rather than every collider in the scene.
52
- * A tree over twenty leaves that all move every frame costs more to keep than
53
- * to skip.
70
+ * ## The planes, which were the third ground and are not a structure
54
71
  *
55
- * And a {@link PlaneShape3D} has an **unbounded** AABB, which a BVH cannot hold
56
- * — every refit would grow the root to infinity and every query would visit
57
- * everything. A linear scan holds an infinity without complaint, because
58
- * `min ≤ other_max` is still an answer when one side is `Infinity`. If a scene
59
- * ever wants thousands of cloth colliders, the shape of the fix is a BVH over
60
- * the bounded ones with the planes kept in a short list beside it.
72
+ * A {@link PlaneShape3D} has an unbounded AABB, and a leaf holding one would
73
+ * grow every ancestor to infinity and make the tree answer every query with
74
+ * everything. That much is true. What it is not is a reason for anything: a
75
+ * half-space is constructed in exactly one place in this engine — the JSON
76
+ * shape adapter — and no scene builds one. So planes are kept out of the tree
77
+ * in {@link #planes} and appended to every answer, which is the correct result
78
+ * for a volume that overlaps every box, and which costs a loop over an array
79
+ * that is empty in every scene anyone has run.
80
+ *
81
+ * ## The answer is sorted, and that is load-bearing
82
+ *
83
+ * A scan answers in ascending handle order for free. A tree answers in
84
+ * traversal order, which changes as the tree rebalances even when the set does
85
+ * not — and `cloth_colliders_moved` compares the gathered keys **element by
86
+ * element** against the last step's, so a reshuffle of an unchanged set would
87
+ * wake every sleeping cloth in the scene. {@link cloth_gather_colliders} also
88
+ * keeps the first `CLOTH_COLLIDER_SLOTS` of an overflow, so the order decides
89
+ * which colliders a crowded cloth loses. Sorting restores both properties
90
+ * exactly as the scan had them.
61
91
  *
62
92
  * ## The previous pose is the index's, not the caller's
63
93
  *
@@ -67,6 +97,24 @@ import { cloth_collider_pack } from "./cloth_collider_pack.js";
67
97
  * written. A collider that has just been added has both poses equal, so its
68
98
  * first step sweeps nothing — which is right: it did not move, it appeared.
69
99
  *
100
+ * ## A retired slot is cold before it is warm again
101
+ *
102
+ * A handle is a slot index, so it is reused — and a cloth's warm-started
103
+ * contacts are keyed on it, dropped when the handle stops being found. That
104
+ * contract only holds if the handle *is* absent for a step: retire a collider
105
+ * and add another in the same tick, and the slot is popped immediately, the new
106
+ * surface is indexable as the old one, and a particle settled on the old floor
107
+ * carries its multiplier and a friction anchor in a frame that no longer exists
108
+ * straight onto the new one. Measured: lambda 0.0981 and the old anchor,
109
+ * unchanged across the swap.
110
+ *
111
+ * So {@link remove} retires into a list that {@link advance} drains, one step
112
+ * later. Every cloth has by then stepped against an index the handle was not in
113
+ * — which is exactly what makes its slot cold — and the cost is one slot held
114
+ * back for one step. A generation word beside the handle would do the same job
115
+ * and would have to be threaded through the per-cloth table, the key array and
116
+ * the warm-start scan; this is the same guarantee for a `push` and a `for`.
117
+ *
70
118
  * @author Alex Goldring
71
119
  * @copyright Company Named Limited (c) 2026
72
120
  */
@@ -96,6 +144,33 @@ export class ClothColliderIndex {
96
144
  */
97
145
  count = 0;
98
146
 
147
+ /**
148
+ * The widest {@link CCR_INFLATION} any live record carries, metres.
149
+ *
150
+ * A gather's margin has to cover what the narrowphase will ask for, and
151
+ * `cloth_contact_find` reaches `(radius + inflation) · (1 + margin)` from
152
+ * each particle. The gather cannot know which colliders it is about to find,
153
+ * so it takes the widest in the index — which is why this is here rather
154
+ * than derived per query.
155
+ *
156
+ * Exact after {@link advance}, and only ever raised between advances by
157
+ * {@link add} and {@link setSurface}. Stale-high is a wider margin; the
158
+ * other direction is a missed contact, so the maximum is never lowered
159
+ * inside a step.
160
+ * @type {number}
161
+ */
162
+ max_inflation = 0;
163
+
164
+ /**
165
+ * The furthest any live collider has moved since {@link advance}, metres.
166
+ *
167
+ * The narrowphase adds each collider's own travel to its reach on top of
168
+ * testing the swept bounds, so the gather's margin has to carry it too or
169
+ * the set it hands over is smaller than the set the rows are drawn from.
170
+ * @type {number}
171
+ */
172
+ max_collider_travel = 0;
173
+
99
174
  /**
100
175
  * Retired slots, reused before the table grows. A handle is a slot index,
101
176
  * so reuse is what keeps handles dense; it also means a handle can name a
@@ -107,6 +182,15 @@ export class ClothColliderIndex {
107
182
  */
108
183
  #free_slots = [];
109
184
 
185
+ /**
186
+ * Slots retired during this step. Drained into {@link #free_slots} by
187
+ * {@link advance}, so a handle is never reused within the step it was
188
+ * retired in — see the class docblock for what that buys.
189
+ * @type {number[]}
190
+ * @private
191
+ */
192
+ #retiring = [];
193
+
110
194
  /**
111
195
  * `true` for a live slot.
112
196
  * @type {Uint8Array}
@@ -114,6 +198,44 @@ export class ClothColliderIndex {
114
198
  */
115
199
  #live = new Uint8Array(0);
116
200
 
201
+ /**
202
+ * The tree over every live record with a bounded AABB. User data is the
203
+ * handle, so a query answers in the index's own names with no indirection.
204
+ * @type {BVH}
205
+ * @private
206
+ */
207
+ #bvh = new BVH();
208
+
209
+ /**
210
+ * Leaf node per handle, or {@link NULL_NODE} for a slot that is dead or
211
+ * holds a half-space.
212
+ * @type {Uint32Array}
213
+ * @private
214
+ */
215
+ #node = new Uint32Array(0);
216
+
217
+ /**
218
+ * Handles of live half-spaces, which cannot go in the tree. Empty in every
219
+ * scene this engine has; see the class docblock.
220
+ * @type {number[]}
221
+ * @private
222
+ */
223
+ #planes = [];
224
+
225
+ /**
226
+ * Handles the tree answered with, before filtering. Grown and reused.
227
+ * @type {Uint32Array}
228
+ * @private
229
+ */
230
+ #candidates = new Uint32Array(256);
231
+
232
+ /**
233
+ * The filtered answer, sorted before it is handed out.
234
+ * @type {number[]}
235
+ * @private
236
+ */
237
+ #answer = [];
238
+
117
239
  /**
118
240
  * Add a collider. The shape is packed once, here; the pose is written by
119
241
  * {@link setPose} and must be before the first query.
@@ -162,13 +284,35 @@ export class ClothColliderIndex {
162
284
  this.table[r + CCR_QW] = 1;
163
285
  this.table[r + CCR_PREV_QW] = 1;
164
286
 
287
+ if (inflation > this.max_inflation) {
288
+ this.max_inflation = inflation;
289
+ }
290
+
291
+ // Identity-posed bounds, so the record is never read as garbage left by
292
+ // the slot's last tenant.
293
+ cloth_collider_refit_bounds(this.table, handle, CCR_STRIDE);
294
+
295
+ this.#node[handle] = NULL_NODE;
296
+
297
+ if (this.table[r + CCR_KIND] === ClothColliderKind.Plane) {
298
+ this.#planes.push(handle);
299
+ }
300
+
301
+ // No leaf yet, on purpose. A record has no pose until {@link setPose},
302
+ // and a leaf inserted here would be homed at the origin: `insert_leaf`
303
+ // picks a sibling by where the box *is*, so a scene's worth of records
304
+ // added before they are placed builds its tree out of one coincident
305
+ // point and then never rebuilds it — {@link BVH#node_move_aabb} refits
306
+ // ancestors and leaves the topology alone. Measured, 16,384 colliders
307
+ // added-then-placed: a root 21 deep whose every branch overlapped every
308
+ // query, and 128 gathers costing 128 ms instead of 0.2.
165
309
  return handle;
166
310
  }
167
311
 
168
312
  /**
169
- * Retire a handle. Its slot is reused by the next {@link add}, and any
170
- * cloth holding warm-started contacts against it drops them on its next
171
- * step because the handle it remembers no longer names the same collider.
313
+ * Retire a handle. Its slot is reused from the **next** {@link advance}
314
+ * onwards, and any cloth holding warm-started contacts against it drops
315
+ * them on its next step because the handle it remembers is no longer found.
172
316
  *
173
317
  * @param {number} handle
174
318
  * @returns {void}
@@ -179,7 +323,22 @@ export class ClothColliderIndex {
179
323
  }
180
324
 
181
325
  this.#live[handle] = 0;
182
- this.#free_slots.push(handle);
326
+ this.#retiring.push(handle);
327
+
328
+ const node = this.#node[handle];
329
+
330
+ if (node === NULL_NODE) {
331
+ const at = this.#planes.indexOf(handle);
332
+
333
+ if (at >= 0) {
334
+ this.#planes.splice(at, 1);
335
+ }
336
+ } else {
337
+ this.#bvh.remove_leaf(node);
338
+ this.#bvh.release_node(node);
339
+
340
+ this.#node[handle] = NULL_NODE;
341
+ }
183
342
 
184
343
  // A retired record must not be found by a query. Its bounds are emptied
185
344
  // rather than its slot compacted, because compacting would move every
@@ -209,6 +368,23 @@ export class ClothColliderIndex {
209
368
  * @returns {void}
210
369
  */
211
370
  advance() {
371
+ const retiring = this.#retiring;
372
+
373
+ // Everything retired during the last step has now been absent from a
374
+ // whole step's worth of queries, so every cloth's slot for it has gone
375
+ // cold and the slot can be handed out again.
376
+ for (let i = 0; i < retiring.length; i++) {
377
+ this.#free_slots.push(retiring[i]);
378
+ }
379
+
380
+ retiring.length = 0;
381
+
382
+ // A new step, so nothing has moved in it yet, and the inflation
383
+ // maximum is re-derived from what is actually live — this is the one
384
+ // place it can come down again after a wide collider retires.
385
+ this.max_collider_travel = 0;
386
+ this.max_inflation = 0;
387
+
212
388
  const table = this.table;
213
389
 
214
390
  for (let handle = 0; handle < this.count; handle++) {
@@ -218,6 +394,10 @@ export class ClothColliderIndex {
218
394
 
219
395
  const r = handle * CCR_STRIDE;
220
396
 
397
+ if (table[r + CCR_INFLATION] > this.max_inflation) {
398
+ this.max_inflation = table[r + CCR_INFLATION];
399
+ }
400
+
221
401
  table[r + CCR_PREV_TX] = table[r + CCR_TX];
222
402
  table[r + CCR_PREV_TY] = table[r + CCR_TY];
223
403
  table[r + CCR_PREV_TZ] = table[r + CCR_TZ];
@@ -264,6 +444,108 @@ export class ClothColliderIndex {
264
444
  table[r + CCR_QW] = qw;
265
445
 
266
446
  cloth_collider_refit_bounds(this.table, handle, CCR_STRIDE);
447
+
448
+ const travel = Math.hypot(
449
+ tx - table[r + CCR_PREV_TX],
450
+ ty - table[r + CCR_PREV_TY],
451
+ tz - table[r + CCR_PREV_TZ]
452
+ );
453
+
454
+ if (travel > this.max_collider_travel) {
455
+ this.max_collider_travel = travel;
456
+ }
457
+
458
+ const node = this.#node[handle];
459
+
460
+ if (node === NULL_NODE) {
461
+ if (table[r + CCR_KIND] === ClothColliderKind.Plane) {
462
+ // A half-space is not in the tree, and its bounds are already
463
+ // the answer to every query that can reach it.
464
+ return;
465
+ }
466
+
467
+ // First pose: this is where the leaf gets homed, and the pose is
468
+ // what `insert_leaf` needs to pick a sibling worth having.
469
+ const fresh = this.#bvh.allocate_node();
470
+
471
+ this.#bvh.node_set_user_data(fresh, handle);
472
+ this.#node[handle] = fresh;
473
+
474
+ this.#store_fat_aabb(fresh, r);
475
+
476
+ this.#bvh.insert_leaf(fresh);
477
+
478
+ return;
479
+ }
480
+
481
+ // The refit skip, and the reason a resting character costs nothing: the
482
+ // stored box is the swept box plus slack, so while the swept box is
483
+ // still inside it the leaf already covers everything this record can be
484
+ // found by. Only a collider that has left its own slack touches the
485
+ // tree.
486
+ this.#bvh.node_get_aabb(node, STORED_AABB);
487
+
488
+ if (table[r + CCR_AABB_MIN_X] >= STORED_AABB[0]
489
+ && table[r + CCR_AABB_MIN_Y] >= STORED_AABB[1]
490
+ && table[r + CCR_AABB_MIN_Z] >= STORED_AABB[2]
491
+ && table[r + CCR_AABB_MAX_X] <= STORED_AABB[3]
492
+ && table[r + CCR_AABB_MAX_Y] <= STORED_AABB[4]
493
+ && table[r + CCR_AABB_MAX_Z] <= STORED_AABB[5]) {
494
+ return;
495
+ }
496
+
497
+ // Re-homed, not refit. {@link BVH#node_move_aabb} grows the ancestors
498
+ // to cover the leaf where it now is and never asks whether it still
499
+ // belongs under them, so a character crossing a level drags a box
500
+ // through branches it has nothing to do with and every query pays for
501
+ // the overlap. Pulling the leaf and letting `insert_leaf` choose again
502
+ // is what keeps the tree worth having, and the slack is what keeps it
503
+ // rare: a collider moving inside its own five centimetres never gets
504
+ // here at all.
505
+ this.#bvh.remove_leaf(node);
506
+
507
+ this.#store_fat_aabb(node, r);
508
+
509
+ this.#bvh.insert_leaf(node);
510
+ }
511
+
512
+ /**
513
+ * Write a record's swept bounds into {@link FAT_AABB} with the slack the
514
+ * refit skip spends, and store them on the leaf.
515
+ *
516
+ * The slack is also what makes the tree's `Float32` nodes safe to hold a
517
+ * `Float64` world, and it has two terms because one is not enough. The
518
+ * constant covers jitter. The relative term covers the rounding
519
+ * {@link BVH#node_set_aabb} does on the way in: a bound can move by an ulp,
520
+ * and `Float32`'s ulp is a *proportion* of the magnitude — a millimetre at
521
+ * ten kilometres out, but **a whole metre at ten thousand**, which is well
522
+ * past the constant: measured, a leaf there comes back 45 cm inside its own
523
+ * record. A bound that rounds inward past its slack is a leaf
524
+ * smaller than the record it stands for, and that is a collider the tree
525
+ * stops answering with — silently, and only far from the origin, which is
526
+ * the worst shape a bug can have. Two ulps of the magnitude puts it back
527
+ * outside at every scale the `Float64` table is kept for.
528
+ *
529
+ * The exact test in {@link query} reads the `Float64` record, so the tree
530
+ * only ever has to avoid a false **negative**; being generous here costs a
531
+ * candidate that is then rejected.
532
+ *
533
+ * @param {number} node
534
+ * @param {number} r the record's slot offset
535
+ * @private
536
+ */
537
+ #store_fat_aabb(node, r) {
538
+ const table = this.table;
539
+
540
+ for (let axis = 0; axis < 3; axis++) {
541
+ const lower = table[r + CCR_AABB_MIN_X + axis];
542
+ const upper = table[r + CCR_AABB_MAX_X + axis];
543
+
544
+ FAT_AABB[axis] = lower - BVH_LEAF_SLACK - Math.abs(lower) * FLOAT32_ROUNDING;
545
+ FAT_AABB[axis + 3] = upper + BVH_LEAF_SLACK + Math.abs(upper) * FLOAT32_ROUNDING;
546
+ }
547
+
548
+ this.#bvh.node_set_aabb(node, FAT_AABB);
267
549
  }
268
550
 
269
551
  /**
@@ -313,6 +595,13 @@ export class ClothColliderIndex {
313
595
 
314
596
  this.table[r + CCR_FRICTION] = friction;
315
597
  this.table[r + CCR_INFLATION] = inflation;
598
+
599
+ // Raised now rather than at the next `advance`, because a gather
600
+ // between the two would otherwise reach less far than the narrowphase
601
+ // it is feeding.
602
+ if (inflation > this.max_inflation) {
603
+ this.max_inflation = inflation;
604
+ }
316
605
  }
317
606
 
318
607
  /**
@@ -356,44 +645,116 @@ export class ClothColliderIndex {
356
645
  * @returns {number} how many handles were written
357
646
  */
358
647
  query(out, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask) {
359
- const table = this.table;
360
- const meta = this.meta;
648
+ QUERY_AABB[0] = min_x;
649
+ QUERY_AABB[1] = min_y;
650
+ QUERY_AABB[2] = min_z;
651
+ QUERY_AABB[3] = max_x;
652
+ QUERY_AABB[4] = max_y;
653
+ QUERY_AABB[5] = max_z;
361
654
 
362
- let found = 0;
655
+ // Grow and retry: the traversal returns the TRUE overlap count and
656
+ // drops writes past the end, so a short buffer is a resize rather than
657
+ // a silently truncated answer. Capping instead would drop the same
658
+ // trailing leaves every step — a permanently invisible collider rather
659
+ // than a slow one.
660
+ let candidates = this.#candidates;
363
661
 
364
- for (let handle = 0; handle < this.count; handle++) {
365
- if (this.#live[handle] === 0) {
366
- continue;
367
- }
662
+ let candidate_count = bvh_query_user_data_intersects_aabb(candidates, 0, this.#bvh, QUERY_AABB);
368
663
 
369
- const m = handle * CCR_META_STRIDE;
664
+ if (candidate_count > candidates.length) {
665
+ candidates = new Uint32Array(candidate_count * 2);
370
666
 
371
- if ((mask & meta[m + CCR_META_LAYER]) === 0) {
372
- continue;
373
- }
667
+ this.#candidates = candidates;
374
668
 
375
- if ((meta[m + CCR_META_MASK] & layer) === 0) {
376
- continue;
377
- }
669
+ candidate_count = bvh_query_user_data_intersects_aabb(candidates, 0, this.#bvh, QUERY_AABB);
670
+ }
378
671
 
379
- const r = handle * CCR_STRIDE;
672
+ const answer = this.#answer;
380
673
 
381
- if (table[r + CCR_AABB_MIN_X] > max_x || table[r + CCR_AABB_MAX_X] < min_x) {
382
- continue;
383
- }
674
+ answer.length = 0;
384
675
 
385
- if (table[r + CCR_AABB_MIN_Y] > max_y || table[r + CCR_AABB_MAX_Y] < min_y) {
386
- continue;
676
+ for (let i = 0; i < candidate_count; i++) {
677
+ const handle = candidates[i];
678
+
679
+ // The leaf carries slack and `Float32` rounding; the record is the
680
+ // exact `Float64` answer, and it is what the narrowphase will read.
681
+ if (this.#overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask)) {
682
+ answer.push(handle);
387
683
  }
684
+ }
388
685
 
389
- if (table[r + CCR_AABB_MIN_Z] > max_z || table[r + CCR_AABB_MAX_Z] < min_z) {
390
- continue;
686
+ const planes = this.#planes;
687
+
688
+ for (let i = 0; i < planes.length; i++) {
689
+ const handle = planes[i];
690
+
691
+ if (this.#overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask)) {
692
+ answer.push(handle);
391
693
  }
694
+ }
392
695
 
393
- out[found++] = handle;
696
+ // Ascending handle order — see the class docblock. The wake test
697
+ // compares element by element and the gather's overflow keeps a prefix,
698
+ // so both read this order as meaning.
699
+ answer.sort(ASCENDING);
700
+
701
+ for (let i = 0; i < answer.length; i++) {
702
+ out[i] = answer[i];
394
703
  }
395
704
 
396
- return found;
705
+ return answer.length;
706
+ }
707
+
708
+ /**
709
+ * Whether a live record passes the filter and its exact swept bounds meet
710
+ * the box.
711
+ *
712
+ * @param {number} handle
713
+ * @param {number} min_x
714
+ * @param {number} min_y
715
+ * @param {number} min_z
716
+ * @param {number} max_x
717
+ * @param {number} max_y
718
+ * @param {number} max_z
719
+ * @param {number} layer
720
+ * @param {number} mask
721
+ * @returns {boolean}
722
+ * @private
723
+ */
724
+ #overlaps(handle, min_x, min_y, min_z, max_x, max_y, max_z, layer, mask) {
725
+ if (this.#live[handle] === 0) {
726
+ return false;
727
+ }
728
+
729
+ const meta = this.meta;
730
+
731
+ const m = handle * CCR_META_STRIDE;
732
+
733
+ if ((mask & meta[m + CCR_META_LAYER]) === 0) {
734
+ return false;
735
+ }
736
+
737
+ if ((meta[m + CCR_META_MASK] & layer) === 0) {
738
+ return false;
739
+ }
740
+
741
+ const table = this.table;
742
+
743
+ const r = handle * CCR_STRIDE;
744
+
745
+ if (table[r + CCR_AABB_MIN_X] > max_x || table[r + CCR_AABB_MAX_X] < min_x) {
746
+ return false;
747
+ }
748
+
749
+ if (table[r + CCR_AABB_MIN_Y] > max_y || table[r + CCR_AABB_MAX_Y] < min_y) {
750
+ return false;
751
+ }
752
+
753
+ if (table[r + CCR_AABB_MIN_Z] > max_z || table[r + CCR_AABB_MAX_Z] < min_z) {
754
+ return false;
755
+ }
756
+
757
+ return true;
397
758
  }
398
759
 
399
760
  /**
@@ -422,14 +783,62 @@ export class ClothColliderIndex {
422
783
  const table = new Float64Array(capacity * CCR_STRIDE);
423
784
  const meta = new Int32Array(capacity * CCR_META_STRIDE);
424
785
  const live = new Uint8Array(capacity);
786
+ const node = new Uint32Array(capacity);
425
787
 
426
788
  table.set(this.table);
427
789
  meta.set(this.meta);
428
790
  live.set(this.#live);
791
+ node.set(this.#node);
792
+
793
+ node.fill(NULL_NODE, this.#node.length);
429
794
 
430
795
  this.table = table;
431
796
  this.meta = meta;
432
797
  this.#live = live;
798
+ this.#node = node;
433
799
  }
434
800
 
435
801
  }
802
+
803
+ /**
804
+ * How far past a record's swept bounds its leaf is stored, metres. The same
805
+ * constant `compute_fat_world_aabb` spends on a rigid body, and for the same
806
+ * reason: a collider jittering inside its own slack must not cost a refit.
807
+ * @type {number}
808
+ */
809
+ const BVH_LEAF_SLACK = 0.05;
810
+
811
+ /**
812
+ * Two `Float32` ulps as a proportion of a value's magnitude — what a bound has
813
+ * to be widened by to survive being stored in a `Float32` leaf whatever the
814
+ * rounding does. `Float32` carries 23 fraction bits, so one ulp is `2^-23` of
815
+ * the magnitude and two is the first multiple that is unconditionally outside.
816
+ * @type {number}
817
+ */
818
+ const FLOAT32_ROUNDING = 2 ** -22;
819
+
820
+ /**
821
+ * @type {Float64Array}
822
+ */
823
+ const QUERY_AABB = new Float64Array(6);
824
+
825
+ /**
826
+ * The box on its way into a leaf.
827
+ * @type {Float64Array}
828
+ */
829
+ const FAT_AABB = new Float64Array(6);
830
+
831
+ /**
832
+ * The tree stores `Float32` bounds, so this is what reads them back.
833
+ * @type {Float32Array}
834
+ */
835
+ const STORED_AABB = new Float32Array(6);
836
+
837
+ /**
838
+ * @param {number} a
839
+ * @param {number} b
840
+ * @returns {number}
841
+ */
842
+ function ASCENDING(a, b) {
843
+ return a - b;
844
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * # Binary serialization for {@link ClothCollider}
3
+ *
4
+ * Five scalars, all of them authored, all of them written. §3.5's component is
5
+ * a marker that opts an existing `(Collider, Transform64)` into the cloth
6
+ * simulation, so everything it does *not* carry — geometry and friction from
7
+ * `Collider`, pose from `Transform64` — is already the concern of those two
8
+ * components' own adapters. There is nothing shared, nothing runtime and
9
+ * nothing to exclude: the packed record, the swept bounds and the handle are
10
+ * `ClothColliderIndex`'s, keyed by entity and rebuilt when
11
+ * `ClothColliderSystem` links the entity, exactly as `_bodyId` is rebuilt on
12
+ * `PhysicsSystem#link`.
13
+ *
14
+ * ## `layer` and `mask` are read back unsigned
15
+ *
16
+ * The pair carries `RigidBody` semantics (§3.5) but not `RigidBody`'s
17
+ * signedness: the default `mask` here is the unsigned `0xFFFFFFFF` and
18
+ * `ClothCollider.from` asserts `isNonNegativeInteger` on it, so the `| 0` that
19
+ * `RigidBodySerializationAdapter` and `ColliderSerializationAdapter` both apply
20
+ * would hand back `-1` and break the component's own contract.
21
+ * {@link ClothSerializationAdapter} has the long form of this, and the two
22
+ * components have to agree about it — a mask filter is evaluated between them.
23
+ *
24
+ * ## Version 0, and no upgrader
25
+ *
26
+ * A first version has no predecessor stream to upgrade from;
27
+ * `RigidBodySerializationUpgrader_0_1` is what a later field addition looks
28
+ * like. The spec's exact-size assertion is what makes a field added without the
29
+ * bump fail rather than silently shift every following component in a stream.
30
+ *
31
+ * @author Alex Goldring
32
+ * @copyright Company Named Limited (c) 2026
33
+ */
34
+ export class ClothColliderSerializationAdapter extends BinaryClassSerializationAdapter<any> {
35
+ constructor();
36
+ klass: typeof ClothCollider;
37
+ /**
38
+ *
39
+ * @param {BinaryBuffer} buffer
40
+ * @param {ClothCollider} value
41
+ */
42
+ serialize(buffer: BinaryBuffer, value: ClothCollider): void;
43
+ /**
44
+ *
45
+ * @param {BinaryBuffer} buffer
46
+ * @param {ClothCollider} value
47
+ */
48
+ deserialize(buffer: BinaryBuffer, value: ClothCollider): void;
49
+ }
50
+ import { BinaryClassSerializationAdapter } from "../../../ecs/storage/binary/BinaryClassSerializationAdapter.js";
51
+ import { ClothCollider } from "./ClothCollider.js";
52
+ //# sourceMappingURL=ClothColliderSerializationAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ClothColliderSerializationAdapter.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH;;IAEI,4BAAsB;IAGtB;;;;OAIG;IACH,uCAFW,aAAa,QASvB;IAED;;;;OAIG;IACH,yCAFW,aAAa,QAUvB;CACJ;gDArE+C,gEAAgE;8BAClF,oBAAoB"}