@woosh/meep-engine 3.24.0 → 3.25.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 (322) hide show
  1. package/package.json +1 -1
  2. package/src/engine/graphics3/PickingSystem.d.ts +3 -3
  3. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
  4. package/src/engine/graphics3/PickingSystem.js +52 -55
  5. package/src/engine/graphics3/shader_picking_gather.d.ts +40 -0
  6. package/src/engine/graphics3/shader_picking_gather.d.ts.map +1 -0
  7. package/src/engine/graphics3/shader_picking_gather.js +120 -0
  8. package/src/engine/physics/cloth/MEASUREMENTS.md +1050 -0
  9. package/src/engine/physics/cloth/PLAN.md +411 -49
  10. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts +52 -0
  11. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts.map +1 -0
  12. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.js +70 -0
  13. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts +12 -3
  14. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts.map +1 -1
  15. package/src/engine/physics/cloth/ecs/ClothDynamics.js +340 -331
  16. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +36 -22
  17. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  18. package/src/engine/physics/cloth/ecs/ClothInstance.js +38 -23
  19. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts +84 -0
  20. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts.map +1 -0
  21. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.js +141 -0
  22. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +51 -82
  23. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  24. package/src/engine/physics/cloth/ecs/ClothSystem.js +81 -869
  25. package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts +48 -0
  26. package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts.map +1 -0
  27. package/src/engine/physics/cloth/ecs/ClothWorkerCore.js +107 -0
  28. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +286 -0
  29. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -0
  30. package/src/engine/physics/cloth/ecs/ClothWorld.js +1104 -0
  31. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +182 -0
  32. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -0
  33. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +562 -0
  34. package/src/engine/physics/cloth/ecs/cloth.worker.d.ts +2 -0
  35. package/src/engine/physics/cloth/ecs/cloth.worker.d.ts.map +1 -0
  36. package/src/engine/physics/cloth/ecs/cloth.worker.js +12 -0
  37. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  38. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +2 -1
  39. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +58 -0
  40. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
  41. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +341 -271
  42. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +20 -2
  43. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  44. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +39 -15
  45. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts +29 -0
  46. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts.map +1 -0
  47. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.js +78 -0
  48. package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts +17 -0
  49. package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts.map +1 -0
  50. package/src/engine/physics/cloth/ecs/makeClothWorker.js +18 -0
  51. package/src/engine/physics/cloth/playground/README.md +73 -2
  52. package/src/engine/physics/cloth/playground/wind.html +166 -0
  53. package/src/engine/physics/cloth/playground/wind_build.d.ts +81 -0
  54. package/src/engine/physics/cloth/playground/wind_build.d.ts.map +1 -0
  55. package/src/engine/physics/cloth/playground/wind_build.js +141 -0
  56. package/src/engine/physics/cloth/playground/wind_main.d.ts +2 -0
  57. package/src/engine/physics/cloth/playground/wind_main.d.ts.map +1 -0
  58. package/src/engine/physics/cloth/playground/wind_main.js +681 -0
  59. package/src/engine/physics/cloth/solver/ClothState.d.ts +134 -79
  60. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  61. package/src/engine/physics/cloth/solver/ClothState.js +273 -12
  62. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +33 -0
  63. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  64. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +34 -0
  65. package/src/engine/physics/cloth/solver/cloth_step.d.ts +21 -2
  66. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  67. package/src/engine/physics/cloth/solver/cloth_step.js +173 -4
  68. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +40 -0
  69. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -0
  70. package/src/engine/physics/cloth/solver/cloth_step_instance.js +284 -0
  71. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts +107 -0
  72. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts.map +1 -0
  73. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.js +263 -0
  74. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +88 -0
  75. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -0
  76. package/src/engine/physics/cloth/wind/AbstractClothWind.js +100 -0
  77. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +85 -0
  78. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -0
  79. package/src/engine/physics/cloth/wind/ClothAmbientWind.js +182 -0
  80. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts +79 -0
  81. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -0
  82. package/src/engine/physics/cloth/wind/ClothFluidWind.js +210 -0
  83. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +101 -0
  84. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -0
  85. package/src/engine/physics/cloth/wind/cloth_sample_air.js +290 -0
  86. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  87. package/src/shade/device/mock/texture_copy.d.ts.map +1 -1
  88. package/src/shade/device/mock/texture_copy.js +28 -0
  89. package/src/shade/playground/vgeo_runtime/README.md +94 -0
  90. package/src/shade/playground/vgeo_runtime/index.html +62 -0
  91. package/src/shade/playground/vgeo_runtime/main.d.ts +2 -0
  92. package/src/shade/playground/vgeo_runtime/main.d.ts.map +1 -0
  93. package/src/shade/playground/vgeo_runtime/main.js +399 -0
  94. package/src/shade/playground/vgeo_viewer/sample_asset.js +1 -1
  95. package/src/shade/renderer/camera/Camera.d.ts +14 -0
  96. package/src/shade/renderer/camera/Camera.d.ts.map +1 -1
  97. package/src/shade/renderer/camera/Camera.js +7 -1
  98. package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.d.ts.map +1 -1
  99. package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.js +16 -1
  100. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  101. package/src/shade/renderer/geometry/GPUGeometryManager.js +29 -9
  102. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts +10 -0
  103. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts.map +1 -1
  104. package/src/shade/renderer/geometry/GPUGeometryMetadata.js +13 -0
  105. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +46 -18
  106. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +70 -22
  107. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +97 -19
  108. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +12 -2
  109. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  110. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +14 -3
  111. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +2 -2
  112. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  113. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +124 -6
  114. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +22 -0
  115. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +1 -0
  116. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.js +53 -0
  117. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +8 -1
  118. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  119. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +8 -1
  120. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts +16 -0
  121. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.js +15 -0
  123. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts +54 -0
  124. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts.map +1 -0
  125. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.js +53 -0
  126. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +13 -2
  127. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  128. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +14 -2
  129. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +100 -0
  130. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  131. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +113 -0
  132. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
  133. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +5 -0
  134. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  135. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +108 -0
  136. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  137. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +23 -0
  138. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  139. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +40 -0
  140. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +228 -0
  141. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -0
  142. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +932 -0
  143. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +176 -0
  144. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -0
  145. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +425 -0
  146. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +241 -0
  147. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +574 -0
  148. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +120 -0
  149. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -0
  150. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +674 -0
  151. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +287 -0
  152. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -0
  153. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +976 -0
  154. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +42 -0
  155. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -0
  156. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +96 -0
  157. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts +16 -0
  158. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts.map +1 -0
  159. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.js +15 -0
  160. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts +19 -0
  161. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -0
  162. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +83 -0
  163. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts +9 -0
  164. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts.map +1 -0
  165. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.js +8 -0
  166. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +19 -0
  167. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -0
  168. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +160 -0
  169. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts +3 -0
  170. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -0
  171. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +255 -0
  172. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts +3 -0
  173. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -0
  174. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +80 -0
  175. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts +3 -0
  176. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -0
  177. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +144 -0
  178. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts +3 -0
  179. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -0
  180. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +530 -0
  181. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +39 -0
  182. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -0
  183. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +54 -0
  184. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts +41 -0
  185. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts.map +1 -0
  186. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.js +56 -0
  187. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts +49 -0
  188. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -0
  189. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +328 -0
  190. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +10 -0
  191. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts.map +1 -0
  192. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +51 -0
  193. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +13 -0
  194. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -0
  195. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +12 -0
  196. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +17 -0
  197. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts.map +1 -0
  198. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +99 -0
  199. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts +9 -0
  200. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts.map +1 -0
  201. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +8 -0
  202. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +7 -0
  203. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts.map +1 -0
  204. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +27 -0
  205. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts +14 -0
  206. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts.map +1 -0
  207. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.js +13 -0
  208. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts +7 -0
  209. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts.map +1 -0
  210. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.js +35 -0
  211. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts +9 -0
  212. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts.map +1 -0
  213. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.js +8 -0
  214. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts +15 -0
  215. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts.map +1 -0
  216. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.js +14 -0
  217. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +7 -0
  218. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts.map +1 -0
  219. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +36 -0
  220. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +10 -0
  221. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -0
  222. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +9 -0
  223. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts +11 -0
  224. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts.map +1 -0
  225. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.js +54 -0
  226. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts +9 -0
  227. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts.map +1 -0
  228. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.js +8 -0
  229. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts +11 -0
  230. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts.map +1 -0
  231. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.js +10 -0
  232. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts +7 -0
  233. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts.map +1 -0
  234. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.js +30 -0
  235. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts +9 -0
  236. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts.map +1 -0
  237. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.js +8 -0
  238. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts +13 -0
  239. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts.map +1 -0
  240. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.js +12 -0
  241. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts +14 -0
  242. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts.map +1 -0
  243. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.js +13 -0
  244. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +11 -0
  245. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts.map +1 -0
  246. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +61 -0
  247. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts +9 -0
  248. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -0
  249. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +8 -0
  250. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts +13 -0
  251. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts.map +1 -0
  252. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.js +12 -0
  253. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts +9 -0
  254. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts.map +1 -0
  255. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.js +8 -0
  256. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts +20 -0
  257. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts.map +1 -0
  258. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.js +19 -0
  259. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts +23 -0
  260. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -0
  261. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +70 -0
  262. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +18 -0
  263. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -0
  264. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +25 -0
  265. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +35 -0
  266. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -0
  267. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +41 -0
  268. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +32 -0
  269. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -0
  270. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +138 -0
  271. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  272. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  273. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  274. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +9 -0
  275. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  276. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +12 -1
  277. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  278. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +12 -1
  279. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  280. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +10 -0
  281. package/src/shade/renderer/scene/GPUSceneContext.d.ts +18 -0
  282. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  283. package/src/shade/renderer/scene/GPUSceneContext.js +647 -594
  284. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  285. package/src/shade/renderer/scene/rows/GPUSceneRows.js +22 -0
  286. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts +35 -0
  287. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts.map +1 -0
  288. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.js +91 -0
  289. package/src/engine/graphics3/instantiate_scene_bundle.d.ts +0 -54
  290. package/src/engine/graphics3/instantiate_scene_bundle.d.ts.map +0 -1
  291. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +0 -39
  292. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts.map +0 -1
  293. package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts +0 -35
  294. package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts.map +0 -1
  295. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts +0 -96
  296. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts.map +0 -1
  297. package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts +0 -20
  298. package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts.map +0 -1
  299. package/src/shade/renderer/animation/skin_bind_transform.d.ts +0 -25
  300. package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +0 -1
  301. package/src/shade/renderer/animation/skin_frame_defect.d.ts +0 -34
  302. package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +0 -1
  303. package/src/shade/renderer/animation/skin_joint_matrix.d.ts +0 -24
  304. package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +0 -1
  305. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +0 -70
  306. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +0 -1
  307. package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts +0 -96
  308. package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts.map +0 -1
  309. package/src/shade/renderer/scene/InstanceBatch.d.ts +0 -92
  310. package/src/shade/renderer/scene/InstanceBatch.d.ts.map +0 -1
  311. package/src/shade/renderer/scene/Mesh.d.ts +0 -55
  312. package/src/shade/renderer/scene/Mesh.d.ts.map +0 -1
  313. package/src/shade/renderer/scene/Node3D.d.ts +0 -159
  314. package/src/shade/renderer/scene/Node3D.d.ts.map +0 -1
  315. package/src/shade/renderer/scene/Node3DDirtyList.d.ts +0 -59
  316. package/src/shade/renderer/scene/Node3DDirtyList.d.ts.map +0 -1
  317. package/src/shade/renderer/scene/SceneNodeBinding.d.ts +0 -52
  318. package/src/shade/renderer/scene/SceneNodeBinding.d.ts.map +0 -1
  319. package/src/shade/renderer/scene/SkinnedMesh.d.ts +0 -27
  320. package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +0 -1
  321. package/src/shade/renderer/scene/TransformAuthority.d.ts +0 -19
  322. package/src/shade/renderer/scene/TransformAuthority.d.ts.map +0 -1
@@ -0,0 +1,210 @@
1
+ import { FluidComponent } from "../../fluid/ecs/FluidComponent.js";
2
+ import { AbstractClothWind } from "./AbstractClothWind.js";
3
+ import { ClothAmbientWind } from "./ClothAmbientWind.js";
4
+
5
+ /**
6
+ * # The air, read out of the fluid simulation that is already running
7
+ *
8
+ * §10's whole argument: this engine has a fluid simulator, `FluidObstacle`
9
+ * already voxelises physics colliders into it, so a character running past a
10
+ * cape gets a **wake** without anything cloth-specific being authored. This is
11
+ * the source that reads it — every enabled {@link FluidComponent} in the scene,
12
+ * sampled trilinearly where a particle actually is.
13
+ *
14
+ * Where no field reaches, {@link ambient} answers instead, so this is the one
15
+ * source a scene needs rather than a thing to switch between: a banner inside a
16
+ * wind tunnel volume reads the tunnel, the same banner carried outside reads the
17
+ * prevailing weather, and the cloth never learns which happened.
18
+ *
19
+ * ## Two units problems, and they are the reason this class exists at all
20
+ *
21
+ * A caller cannot simply forward `FluidComponent#sampleVelocityAtWorld`:
22
+ *
23
+ * - **Cells, not metres.** `FluidField#sampleVelocity` answers in *grid cells*
24
+ * per second and says so — it "does NOT rescale them, on the assumption that
25
+ * callers (vegetation sway, dust drift) interpret the field as a direction ×
26
+ * magnitude wind". Cloth's drag row is a relaxation toward a **velocity**, so
27
+ * the same wind voxelised at 0.25 m instead of 1 m would blow a garment four
28
+ * times as hard. The multiply by `cell_size` is here. `MEASUREMENTS.md` §45.
29
+ * - **Unbuilt fields read as null.** A `FluidComponent` exists from the moment
30
+ * the entity is spawned and its field's buffers arrive at `build()`. Sampling
31
+ * before that is a null dereference rather than a zero, so an unbuilt field is
32
+ * skipped exactly like a disabled one.
33
+ *
34
+ * ## The field list is rebuilt once a step, not once a cloth
35
+ *
36
+ * {@link begin} walks the dataset and caches each live field with its world
37
+ * box. Every cloth's {@link varies} and every particle's {@link sample} then
38
+ * read that cache, so a scene with twenty garments and one wind volume walks the
39
+ * dataset once. `FluidSystem` does the equivalent per field for its effectors
40
+ * and notes the same trade.
41
+ *
42
+ * ## What it does not do
43
+ *
44
+ * **It does not blend at a field's edge.** A point inside a field's box reads
45
+ * the field and a point outside reads the ambient, so a garment straddling the
46
+ * boundary sees a step in the air rather than a ramp. In a scene where the
47
+ * ambient is the far field the volume relaxes toward — `GlobalFluidEffector`'s
48
+ * `wind` — the step is small and mostly the disturbance the volume exists to
49
+ * carry; where it is not, it is visible, and the answer is a volume large enough
50
+ * to hold what moves through it. **Overlapping fields are resolved by order**,
51
+ * first match wins, which is the same arbitrary-but-deterministic rule
52
+ * `FluidSystem` applies when it steps them independently.
53
+ *
54
+ * @author Alex Goldring
55
+ * @copyright Company Named Limited (c) 2026
56
+ */
57
+ export class ClothFluidWind extends AbstractClothWind {
58
+
59
+ /**
60
+ * What the air is where no field reaches. Present rather than optional
61
+ * because "outside every fluid volume" is most of the world.
62
+ * @type {ClothAmbientWind}
63
+ */
64
+ ambient = new ClothAmbientWind();
65
+
66
+ /**
67
+ * Live fields this step: the component and its world box, six words each.
68
+ * @type {FluidComponent[]}
69
+ * @private
70
+ */
71
+ __components = [];
72
+
73
+ /**
74
+ * @type {number[]}
75
+ * @private
76
+ */
77
+ __bounds = [];
78
+
79
+ /**
80
+ * @type {number}
81
+ * @private
82
+ */
83
+ __count = 0;
84
+
85
+ /**
86
+ * @param {number} time
87
+ * @param {EntityComponentDataset} ecd
88
+ * @returns {void}
89
+ */
90
+ begin(time, ecd) {
91
+ this.ambient.begin(time, ecd);
92
+
93
+ const components = this.__components;
94
+ const bounds = this.__bounds;
95
+
96
+ let count = 0;
97
+
98
+ if (ecd !== null && ecd !== undefined) {
99
+ ecd.traverseComponents(FluidComponent, component => {
100
+ if (!component.enabled) {
101
+ return;
102
+ }
103
+
104
+ const field = component.field;
105
+
106
+ if (field.velocity_x === null) {
107
+ // Not built yet — `setResolution`/`build` has not run, so
108
+ // there are no buffers to sample.
109
+ return;
110
+ }
111
+
112
+ const resolution = field.getResolution();
113
+
114
+ const cell_size = component.cell_size;
115
+
116
+ const origin = component.origin;
117
+
118
+ const b = count * 6;
119
+
120
+ bounds[b] = origin[0];
121
+ bounds[b + 1] = origin[1];
122
+ bounds[b + 2] = origin[2];
123
+ bounds[b + 3] = origin[0] + resolution[0] * cell_size;
124
+ bounds[b + 4] = origin[1] + resolution[1] * cell_size;
125
+ bounds[b + 5] = origin[2] + resolution[2] * cell_size;
126
+
127
+ components[count] = component;
128
+
129
+ count++;
130
+ });
131
+ }
132
+
133
+ // Drop references to components that are no longer live, so a despawned
134
+ // fluid volume is not kept alive by this cache.
135
+ for (let i = count; i < components.length; i++) {
136
+ components[i] = null;
137
+ }
138
+
139
+ this.__count = count;
140
+ }
141
+
142
+ /**
143
+ * @param {number} min_x
144
+ * @param {number} min_y
145
+ * @param {number} min_z
146
+ * @param {number} max_x
147
+ * @param {number} max_y
148
+ * @param {number} max_z
149
+ * @returns {boolean} whether a fluid field reaches into this box — and so
150
+ * whether the caller has to sample per particle rather than once
151
+ */
152
+ varies(min_x, min_y, min_z, max_x, max_y, max_z) {
153
+ const count = this.__count;
154
+
155
+ const bounds = this.__bounds;
156
+
157
+ for (let i = 0; i < count; i++) {
158
+ const b = i * 6;
159
+
160
+ if (max_x >= bounds[b]
161
+ && max_y >= bounds[b + 1]
162
+ && max_z >= bounds[b + 2]
163
+ && min_x <= bounds[b + 3]
164
+ && min_y <= bounds[b + 4]
165
+ && min_z <= bounds[b + 5]) {
166
+ return true;
167
+ }
168
+ }
169
+
170
+ return false;
171
+ }
172
+
173
+ /**
174
+ * @param {Float64Array|number[]} out
175
+ * @param {number} x
176
+ * @param {number} y
177
+ * @param {number} z
178
+ * @returns {Float64Array|number[]}
179
+ */
180
+ sample(out, x, y, z) {
181
+ const count = this.__count;
182
+
183
+ const bounds = this.__bounds;
184
+
185
+ for (let i = 0; i < count; i++) {
186
+ const b = i * 6;
187
+
188
+ if (x < bounds[b] || x > bounds[b + 3]
189
+ || y < bounds[b + 1] || y > bounds[b + 4]
190
+ || z < bounds[b + 2] || z > bounds[b + 5]) {
191
+ continue;
192
+ }
193
+
194
+ const component = this.__components[i];
195
+
196
+ component.sampleVelocityAtWorld(out, x, y, z);
197
+
198
+ // Cells per second to metres per second. See the header.
199
+ const cell_size = component.cell_size;
200
+
201
+ out[0] *= cell_size;
202
+ out[1] *= cell_size;
203
+ out[2] *= cell_size;
204
+
205
+ return out;
206
+ }
207
+
208
+ return this.ambient.sample(out, x, y, z);
209
+ }
210
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * # Reading the air into the cloth's own frame
3
+ *
4
+ * The one piece of §10 that has to run on the **main thread**, and the milestone's
5
+ * design decision. M5 moved every number the solver touches onto a worker; a
6
+ * fluid field is not one of those numbers. It is an ECS component holding a
7
+ * hundred thousand floats that the fluid solver rewrites every step, it may
8
+ * itself already live on a *different* worker (`WorkerFluidSystem`), and nothing
9
+ * about it is shaped like a cloth. Sending it across per step would cost more
10
+ * than the whole cloth step it was supposed to feed.
11
+ *
12
+ * So the split is the one §12 already draws — "arbitrary user-facing work —
13
+ * component reads, `Transform64` sync, index queries — stays on the main thread;
14
+ * the pure numeric step crosses" — applied once more. A field query is an index
15
+ * query. What crosses is the **answer**: one air velocity per particle, written
16
+ * into {@link ClothState#air_velocity}, which is a region of the cloth's own
17
+ * buffer and therefore already shared memory. Nothing is copied and nothing is
18
+ * marshalled; the main thread writes exactly where the worker reads. §39's "one
19
+ * buffer per cloth" is what makes that free.
20
+ *
21
+ * The **drag arithmetic** is not here, deliberately. It reads positions and
22
+ * velocities the state already holds, so it belongs with the rest of the
23
+ * arithmetic in `cloth_step_instance` — which keeps §40's property intact:
24
+ * neither system computes a step parameter of its own, the drag response crosses
25
+ * as one more word of the block, and the acceleration is built by the one
26
+ * function both systems call. `MEASUREMENTS.md` §45.
27
+ *
28
+ * ## Two costs, and only one of them scales
29
+ *
30
+ * A source that answers `varies() === false` — {@link ClothAmbientWind}, and
31
+ * {@link ClothFluidWind} anywhere its fields do not reach — is sampled **once**
32
+ * for the whole cloth. What is left is a bounds scan and a fill, both linear in
33
+ * particles with a handful of operations each, and both an order below a
34
+ * trilinear sample. That is the case §10 calls "no fluid field in range", and it
35
+ * is the common one. `MEASUREMENTS.md` §47 is what each costs.
36
+ *
37
+ * @author Alex Goldring
38
+ * @copyright Company Named Limited (c) 2026
39
+ */
40
+ /**
41
+ * Fill {@link ClothState#air_velocity} with the air each particle sits in,
42
+ * rotated into the cloth's local frame.
43
+ *
44
+ * @param {ClothState} state
45
+ * @param {AbstractClothWind} wind
46
+ * @param {Float64Array} anchor_translation the cloth frame's world origin
47
+ * @param {Float64Array} anchor_rotation the cloth frame's world rotation, xyzw
48
+ * @param {Float64Array} [centre] written with the world-space air at the cloth's
49
+ * own centre — what the sleep test compares against next step
50
+ * @returns {boolean} whether the source was sampled per particle
51
+ */
52
+ export function cloth_sample_air(state: ClothState, wind: AbstractClothWind, anchor_translation: Float64Array, anchor_rotation: Float64Array, centre?: Float64Array): boolean;
53
+ /**
54
+ * Whether the air at the cloth's centre has moved far enough since the last step
55
+ * that actually applied it to be worth waking for — §10's "sleep wakes on a wind
56
+ * change, so a settled banner does not sleep through a gust".
57
+ *
58
+ * The comparison is against the last **applied** air rather than the last
59
+ * sampled one, so a gust that arrives slowly still wakes the cloth: the
60
+ * difference accumulates instead of being reset by every step that declined to
61
+ * act on it.
62
+ *
63
+ * The threshold is the sleep test's own speed ratio, and that is deliberately
64
+ * conservative — the velocity a wind change of `Δu` actually produces in one
65
+ * step is `(1 − e^(−rate·dt))·facing·Δu`, a fraction of it, so this wakes on a
66
+ * change several times smaller than the one that would keep the cloth awake.
67
+ * Erring that way is the point: the failure §10 names is a banner sleeping
68
+ * through a gust, not one waking a few steps early.
69
+ *
70
+ * **Its limit, stated.** One point, at the cloth's centre. A wake that reaches
71
+ * only the hem of a sleeping banner and leaves its middle still will not wake
72
+ * it. The collider wake test next to it has no such gap because a collider's
73
+ * pose is seven exact numbers and a field is a continuum — the honest fix is
74
+ * more sample points, and the measurement is that one is enough for anything
75
+ * that crosses a garment rather than grazing it.
76
+ *
77
+ * @param {ClothState} state
78
+ * @param {AbstractClothWind} wind
79
+ * @param {Float64Array} anchor_translation
80
+ * @param {Float64Array} anchor_rotation
81
+ * @param {Float64Array} previous the air last applied to this cloth, world-space
82
+ * @param {number} threshold m/s
83
+ * @returns {boolean}
84
+ */
85
+ export function cloth_air_changed(state: ClothState, wind: AbstractClothWind, anchor_translation: Float64Array, anchor_rotation: Float64Array, previous: Float64Array, threshold: number): boolean;
86
+ /**
87
+ * The cloth's world-space axis-aligned bounds.
88
+ *
89
+ * The particles are local, so this is the bounds of the **rotated** local box
90
+ * rather than the rotation of its bounds — the same eight-corner argument
91
+ * `cloth_gather_colliders` makes for its own query volume, and the same reason:
92
+ * a rotated box is not a box.
93
+ *
94
+ * @param {ClothState} state
95
+ * @param {Float64Array} anchor_translation
96
+ * @param {Float64Array} anchor_rotation
97
+ * @param {Float64Array} out six words, min then max
98
+ * @returns {void}
99
+ */
100
+ export function cloth_air_bounds(state: ClothState, anchor_translation: Float64Array, anchor_rotation: Float64Array, out: Float64Array): void;
101
+ //# sourceMappingURL=cloth_sample_air.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloth_sample_air.d.ts","sourceRoot":"","sources":["../../../../../../src/engine/physics/cloth/wind/cloth_sample_air.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;;;;;;;;;GAWG;AACH,iGANW,YAAY,mBACZ,YAAY,WACZ,YAAY,GAEV,OAAO,CA6EnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,kGANW,YAAY,mBACZ,YAAY,YACZ,YAAY,aACZ,MAAM,GACJ,OAAO,CAqBnB;AAED;;;;;;;;;;;;;GAaG;AACH,wEALW,YAAY,mBACZ,YAAY,OACZ,YAAY,GACV,IAAI,CAoEhB"}
@@ -0,0 +1,290 @@
1
+ import { v3_quaternion_apply } from "../../../../core/geom/vec3/v3_quaternion_apply.js";
2
+ import { v3_quaternion_apply_inverse } from "../../../../core/geom/vec3/v3_quaternion_apply_inverse.js";
3
+
4
+ /**
5
+ * # Reading the air into the cloth's own frame
6
+ *
7
+ * The one piece of §10 that has to run on the **main thread**, and the milestone's
8
+ * design decision. M5 moved every number the solver touches onto a worker; a
9
+ * fluid field is not one of those numbers. It is an ECS component holding a
10
+ * hundred thousand floats that the fluid solver rewrites every step, it may
11
+ * itself already live on a *different* worker (`WorkerFluidSystem`), and nothing
12
+ * about it is shaped like a cloth. Sending it across per step would cost more
13
+ * than the whole cloth step it was supposed to feed.
14
+ *
15
+ * So the split is the one §12 already draws — "arbitrary user-facing work —
16
+ * component reads, `Transform64` sync, index queries — stays on the main thread;
17
+ * the pure numeric step crosses" — applied once more. A field query is an index
18
+ * query. What crosses is the **answer**: one air velocity per particle, written
19
+ * into {@link ClothState#air_velocity}, which is a region of the cloth's own
20
+ * buffer and therefore already shared memory. Nothing is copied and nothing is
21
+ * marshalled; the main thread writes exactly where the worker reads. §39's "one
22
+ * buffer per cloth" is what makes that free.
23
+ *
24
+ * The **drag arithmetic** is not here, deliberately. It reads positions and
25
+ * velocities the state already holds, so it belongs with the rest of the
26
+ * arithmetic in `cloth_step_instance` — which keeps §40's property intact:
27
+ * neither system computes a step parameter of its own, the drag response crosses
28
+ * as one more word of the block, and the acceleration is built by the one
29
+ * function both systems call. `MEASUREMENTS.md` §45.
30
+ *
31
+ * ## Two costs, and only one of them scales
32
+ *
33
+ * A source that answers `varies() === false` — {@link ClothAmbientWind}, and
34
+ * {@link ClothFluidWind} anywhere its fields do not reach — is sampled **once**
35
+ * for the whole cloth. What is left is a bounds scan and a fill, both linear in
36
+ * particles with a handful of operations each, and both an order below a
37
+ * trilinear sample. That is the case §10 calls "no fluid field in range", and it
38
+ * is the common one. `MEASUREMENTS.md` §47 is what each costs.
39
+ *
40
+ * @author Alex Goldring
41
+ * @copyright Company Named Limited (c) 2026
42
+ */
43
+
44
+ /**
45
+ * Fill {@link ClothState#air_velocity} with the air each particle sits in,
46
+ * rotated into the cloth's local frame.
47
+ *
48
+ * @param {ClothState} state
49
+ * @param {AbstractClothWind} wind
50
+ * @param {Float64Array} anchor_translation the cloth frame's world origin
51
+ * @param {Float64Array} anchor_rotation the cloth frame's world rotation, xyzw
52
+ * @param {Float64Array} [centre] written with the world-space air at the cloth's
53
+ * own centre — what the sleep test compares against next step
54
+ * @returns {boolean} whether the source was sampled per particle
55
+ */
56
+ export function cloth_sample_air(state, wind, anchor_translation, anchor_rotation, centre = undefined) {
57
+ const particle_count = state.particle_count;
58
+
59
+ const air = state.air_velocity;
60
+
61
+ if (particle_count === 0) {
62
+ return false;
63
+ }
64
+
65
+ const position = state.position;
66
+
67
+ const anchor_x = anchor_translation[0];
68
+ const anchor_y = anchor_translation[1];
69
+ const anchor_z = anchor_translation[2];
70
+
71
+ const qx = anchor_rotation[0];
72
+ const qy = anchor_rotation[1];
73
+ const qz = anchor_rotation[2];
74
+ const qw = anchor_rotation[3];
75
+
76
+ cloth_air_bounds(state, anchor_translation, anchor_rotation, BOUNDS);
77
+
78
+ const varies = wind.varies(BOUNDS[0], BOUNDS[1], BOUNDS[2], BOUNDS[3], BOUNDS[4], BOUNDS[5]);
79
+
80
+ // The cloth's own centre: where a uniform source is read, and where the
81
+ // sleep test asks whether the air has changed.
82
+ wind.sample(
83
+ SAMPLE,
84
+ (BOUNDS[0] + BOUNDS[3]) * 0.5,
85
+ (BOUNDS[1] + BOUNDS[4]) * 0.5,
86
+ (BOUNDS[2] + BOUNDS[5]) * 0.5
87
+ );
88
+
89
+ if (centre !== undefined) {
90
+ centre[0] = SAMPLE[0];
91
+ centre[1] = SAMPLE[1];
92
+ centre[2] = SAMPLE[2];
93
+ }
94
+
95
+ if (!varies) {
96
+ // One vector for the whole cloth. `varies() === false` is a promise
97
+ // rather than a hint, so this is exact rather than an approximation the
98
+ // caller opted into.
99
+ v3_quaternion_apply_inverse(LOCAL, 0, SAMPLE[0], SAMPLE[1], SAMPLE[2], qx, qy, qz, qw);
100
+
101
+ const local_x = LOCAL[0];
102
+ const local_y = LOCAL[1];
103
+ const local_z = LOCAL[2];
104
+
105
+ for (let i = 0; i < particle_count; i++) {
106
+ const p = i * 3;
107
+
108
+ air[p] = local_x;
109
+ air[p + 1] = local_y;
110
+ air[p + 2] = local_z;
111
+ }
112
+
113
+ return false;
114
+ }
115
+
116
+ for (let i = 0; i < particle_count; i++) {
117
+ const p = i * 3;
118
+
119
+ v3_quaternion_apply(WORLD, 0, position[p], position[p + 1], position[p + 2], qx, qy, qz, qw);
120
+
121
+ wind.sample(SAMPLE, WORLD[0] + anchor_x, WORLD[1] + anchor_y, WORLD[2] + anchor_z);
122
+
123
+ v3_quaternion_apply_inverse(LOCAL, 0, SAMPLE[0], SAMPLE[1], SAMPLE[2], qx, qy, qz, qw);
124
+
125
+ air[p] = LOCAL[0];
126
+ air[p + 1] = LOCAL[1];
127
+ air[p + 2] = LOCAL[2];
128
+ }
129
+
130
+ return true;
131
+ }
132
+
133
+ /**
134
+ * Whether the air at the cloth's centre has moved far enough since the last step
135
+ * that actually applied it to be worth waking for — §10's "sleep wakes on a wind
136
+ * change, so a settled banner does not sleep through a gust".
137
+ *
138
+ * The comparison is against the last **applied** air rather than the last
139
+ * sampled one, so a gust that arrives slowly still wakes the cloth: the
140
+ * difference accumulates instead of being reset by every step that declined to
141
+ * act on it.
142
+ *
143
+ * The threshold is the sleep test's own speed ratio, and that is deliberately
144
+ * conservative — the velocity a wind change of `Δu` actually produces in one
145
+ * step is `(1 − e^(−rate·dt))·facing·Δu`, a fraction of it, so this wakes on a
146
+ * change several times smaller than the one that would keep the cloth awake.
147
+ * Erring that way is the point: the failure §10 names is a banner sleeping
148
+ * through a gust, not one waking a few steps early.
149
+ *
150
+ * **Its limit, stated.** One point, at the cloth's centre. A wake that reaches
151
+ * only the hem of a sleeping banner and leaves its middle still will not wake
152
+ * it. The collider wake test next to it has no such gap because a collider's
153
+ * pose is seven exact numbers and a field is a continuum — the honest fix is
154
+ * more sample points, and the measurement is that one is enough for anything
155
+ * that crosses a garment rather than grazing it.
156
+ *
157
+ * @param {ClothState} state
158
+ * @param {AbstractClothWind} wind
159
+ * @param {Float64Array} anchor_translation
160
+ * @param {Float64Array} anchor_rotation
161
+ * @param {Float64Array} previous the air last applied to this cloth, world-space
162
+ * @param {number} threshold m/s
163
+ * @returns {boolean}
164
+ */
165
+ export function cloth_air_changed(state, wind, anchor_translation, anchor_rotation, previous, threshold) {
166
+ if (state.particle_count === 0) {
167
+ return false;
168
+ }
169
+
170
+ cloth_air_bounds(state, anchor_translation, anchor_rotation, BOUNDS);
171
+
172
+ wind.sample(
173
+ SAMPLE,
174
+ (BOUNDS[0] + BOUNDS[3]) * 0.5,
175
+ (BOUNDS[1] + BOUNDS[4]) * 0.5,
176
+ (BOUNDS[2] + BOUNDS[5]) * 0.5
177
+ );
178
+
179
+ const dx = SAMPLE[0] - previous[0];
180
+ const dy = SAMPLE[1] - previous[1];
181
+ const dz = SAMPLE[2] - previous[2];
182
+
183
+ return dx * dx + dy * dy + dz * dz > threshold * threshold;
184
+ }
185
+
186
+ /**
187
+ * The cloth's world-space axis-aligned bounds.
188
+ *
189
+ * The particles are local, so this is the bounds of the **rotated** local box
190
+ * rather than the rotation of its bounds — the same eight-corner argument
191
+ * `cloth_gather_colliders` makes for its own query volume, and the same reason:
192
+ * a rotated box is not a box.
193
+ *
194
+ * @param {ClothState} state
195
+ * @param {Float64Array} anchor_translation
196
+ * @param {Float64Array} anchor_rotation
197
+ * @param {Float64Array} out six words, min then max
198
+ * @returns {void}
199
+ */
200
+ export function cloth_air_bounds(state, anchor_translation, anchor_rotation, out) {
201
+ const particle_count = state.particle_count;
202
+
203
+ const position = state.position;
204
+
205
+ let min_x = Infinity;
206
+ let min_y = Infinity;
207
+ let min_z = Infinity;
208
+ let max_x = -Infinity;
209
+ let max_y = -Infinity;
210
+ let max_z = -Infinity;
211
+
212
+ for (let i = 0; i < particle_count; i++) {
213
+ const p = i * 3;
214
+
215
+ const x = position[p];
216
+ const y = position[p + 1];
217
+ const z = position[p + 2];
218
+
219
+ if (x < min_x) { min_x = x; }
220
+ if (y < min_y) { min_y = y; }
221
+ if (z < min_z) { min_z = z; }
222
+ if (x > max_x) { max_x = x; }
223
+ if (y > max_y) { max_y = y; }
224
+ if (z > max_z) { max_z = z; }
225
+ }
226
+
227
+ const qx = anchor_rotation[0];
228
+ const qy = anchor_rotation[1];
229
+ const qz = anchor_rotation[2];
230
+ const qw = anchor_rotation[3];
231
+
232
+ let world_min_x = Infinity;
233
+ let world_min_y = Infinity;
234
+ let world_min_z = Infinity;
235
+ let world_max_x = -Infinity;
236
+ let world_max_y = -Infinity;
237
+ let world_max_z = -Infinity;
238
+
239
+ for (let corner = 0; corner < 8; corner++) {
240
+ v3_quaternion_apply(
241
+ WORLD, 0,
242
+ (corner & 1) === 0 ? min_x : max_x,
243
+ (corner & 2) === 0 ? min_y : max_y,
244
+ (corner & 4) === 0 ? min_z : max_z,
245
+ qx, qy, qz, qw
246
+ );
247
+
248
+ const x = WORLD[0] + anchor_translation[0];
249
+ const y = WORLD[1] + anchor_translation[1];
250
+ const z = WORLD[2] + anchor_translation[2];
251
+
252
+ if (x < world_min_x) { world_min_x = x; }
253
+ if (y < world_min_y) { world_min_y = y; }
254
+ if (z < world_min_z) { world_min_z = z; }
255
+ if (x > world_max_x) { world_max_x = x; }
256
+ if (y > world_max_y) { world_max_y = y; }
257
+ if (z > world_max_z) { world_max_z = z; }
258
+ }
259
+
260
+ out[0] = world_min_x;
261
+ out[1] = world_min_y;
262
+ out[2] = world_min_z;
263
+ out[3] = world_max_x;
264
+ out[4] = world_max_y;
265
+ out[5] = world_max_z;
266
+ }
267
+
268
+ /**
269
+ * @type {Float64Array}
270
+ * @private
271
+ */
272
+ const BOUNDS = new Float64Array(6);
273
+
274
+ /**
275
+ * @type {Float64Array}
276
+ * @private
277
+ */
278
+ const WORLD = new Float64Array(3);
279
+
280
+ /**
281
+ * @type {Float64Array}
282
+ * @private
283
+ */
284
+ const LOCAL = new Float64Array(3);
285
+
286
+ /**
287
+ * @type {Float64Array}
288
+ * @private
289
+ */
290
+ const SAMPLE = new Float64Array(3);
@@ -64,11 +64,11 @@ export class FluidObstacleSystem extends System<any> {
64
64
  /**
65
65
  * @param {FluidComponent} fluid
66
66
  */
67
- static "__#391@#refresh_masks"(fluid: FluidComponent): void;
67
+ static "__#396@#refresh_masks"(fluid: FluidComponent): void;
68
68
  /**
69
69
  * @param {FluidComponent} fluid
70
70
  */
71
- static "__#391@#clear_field"(fluid: FluidComponent): void;
71
+ static "__#396@#clear_field"(fluid: FluidComponent): void;
72
72
  /**
73
73
  * Mark every cell of `fluid` whose centre lies within `inflation` of the
74
74
  * posed shape as solid. Iteration is clipped to the shape's world AABB
@@ -80,7 +80,7 @@ export class FluidObstacleSystem extends System<any> {
80
80
  * @param {number} inflation world-units SDF threshold
81
81
  * @param {Float64Array} point length-3 scratch
82
82
  */
83
- static "__#391@#voxelize"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array): void;
83
+ static "__#396@#voxelize"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array): void;
84
84
  /**
85
85
  * Write the obstacle's translation velocity onto every face of every cell
86
86
  * it voxelized — the moving-wall boundary condition. Runs AFTER the mask
@@ -100,7 +100,7 @@ export class FluidObstacleSystem extends System<any> {
100
100
  * @param {number} wvy
101
101
  * @param {number} wvz
102
102
  */
103
- static "__#391@#stamp_wall_velocity"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array, wvx: number, wvy: number, wvz: number): void;
103
+ static "__#396@#stamp_wall_velocity"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array, wvx: number, wvy: number, wvz: number): void;
104
104
  constructor();
105
105
  dependencies: (typeof FluidObstacle)[];
106
106
  components_used: (ResourceAccessSpecification<typeof Transform64> | ResourceAccessSpecification<typeof RigidBody> | ResourceAccessSpecification<typeof Collider> | ResourceAccessSpecification<typeof FluidComponent> | ResourceAccessSpecification<typeof FluidObstacle>)[];
@@ -1 +1 @@
1
- {"version":3,"file":"texture_copy.d.ts","sourceRoot":"","sources":["../../../../../src/shade/device/mock/texture_copy.js"],"names":[],"mappings":"AAiLA;;;;;;;;;;;;;GAaG;AACH;IAP0C,cAAc;IACf,aAAa;IACZ,mBAAmB;IACpB,kBAAkB;IAC9B,IAAI;IACpB,MAAM,GAAC,IAAI,CA4DvB;AAiED;;;;;GAKG;AACH,uHAHW,MAAM,GACJ,IAAI,CA8BhB;;;;;;UA5Fa,WAAW;;;;YACX,MAAM;gBACN,MAAM;kBACN,MAAM"}
1
+ {"version":3,"file":"texture_copy.d.ts","sourceRoot":"","sources":["../../../../../src/shade/device/mock/texture_copy.js"],"names":[],"mappings":"AA6MA;;;;;;;;;;;;;GAaG;AACH;IAP0C,cAAc;IACf,aAAa;IACZ,mBAAmB;IACpB,kBAAkB;IAC9B,IAAI;IACpB,MAAM,GAAC,IAAI,CA4DvB;AAiED;;;;;GAKG;AACH,uHAHW,MAAM,GACJ,IAAI,CA8BhB;;;;;;UA5Fa,WAAW;;;;YACX,MAAM;gBACN,MAAM;kBACN,MAAM"}
@@ -104,6 +104,34 @@ function texture_endpoint_refusal(info, role, required_usage, required_usage_nam
104
104
  return `${role} copySize.height (${extent.height}) is not a multiple of the ${format.block_height}-texel block height of ${texture.format}`;
105
105
  }
106
106
 
107
+ /*
108
+ WebGPU's rule for `copyTextureToBuffer`/`copyBufferToTexture`/`writeTexture`: "if the
109
+ GPUTexture.format is a depth-or-stencil format or GPUTexture.sampleCount is more than 1, the
110
+ subresource size is equal to size". A depth buffer is copied whole or not at all — there is no
111
+ reading one texel of one, and the one-pixel read that looks like the obvious way to pick an
112
+ entity off the frame's depth is the thing this forbids. The restriction comes from D3D12, so
113
+ it is enforced everywhere rather than being a Windows-only surprise.
114
+
115
+ Only the two axes a region is named in: this does not also require a layered copy to take every
116
+ layer, which WebGPU's definition of a subresource's size does.
117
+ */
118
+ const depth_or_stencil = !format.aspects.includes("all");
119
+
120
+ if (depth_or_stencil || texture.sampleCount > 1) {
121
+ const full_width = texture.mip_width(mip);
122
+ const full_height = texture.mip_height(mip);
123
+
124
+ if (origin.x !== 0 || origin.y !== 0 || extent.width !== full_width || extent.height !== full_height) {
125
+ const why = depth_or_stencil
126
+ ? `is ${texture.format}, a depth-or-stencil format`
127
+ : `has sampleCount ${texture.sampleCount}`;
128
+
129
+ return `${role} texture '${texture.label}' ${why}, so a copy must cover the whole`
130
+ + ` subresource; mip ${mip} is ${full_width}x${full_height} and the region asked for`
131
+ + ` is ${extent.width}x${extent.height} at (${origin.x}, ${origin.y})`;
132
+ }
133
+ }
134
+
107
135
  return null;
108
136
  }
109
137