@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
@@ -0,0 +1,681 @@
1
+ import { EntityComponentDataset } from "../../../ecs/EntityComponentDataset.js";
2
+ import { FluidComponent } from "../../fluid/ecs/FluidComponent.js";
3
+ import { fluid_build_world_to_grid } from "../../fluid/ecs/fluid_build_world_to_grid.js";
4
+ import { CLOTH_DYNAMICS_BY_NAME } from "../ecs/cloth_dynamics_library.js";
5
+ import { cloth_bend_ratio, cloth_drag_response } from "../ecs/cloth_dynamics_map.js";
6
+ import { cloth_step } from "../solver/cloth_step.js";
7
+ import { cloth_wind_accelerate } from "../solver/cloth_wind_accelerate.js";
8
+ import { find_non_finite_cloth_state } from "../solver/find_non_finite_cloth_state.js";
9
+ import { ClothFluidWind } from "../wind/ClothFluidWind.js";
10
+ import { cloth_sample_air } from "../wind/cloth_sample_air.js";
11
+ import { build_cloth_playground_scene } from "./cloth_playground_build.js";
12
+ import { draw_cloth, PlaygroundCamera, project } from "./cloth_playground_draw.js";
13
+ import { build_wind_tunnel, place_wind_post } from "./wind_build.js";
14
+
15
+ /**
16
+ * # Cloth wind playground — M6
17
+ *
18
+ * `npm run dev --workspace @woosh/meep-engine`, then
19
+ * <http://localhost:5173/src/engine/physics/cloth/playground/wind.html>.
20
+ *
21
+ * §17 asks M6 for "a playground with a banner in a simulated wake", and the
22
+ * word that matters is **simulated**. There is no wind model on this page. There
23
+ * is a `FluidField`, a `FluidSimulator`, a `GlobalFluidEffector` driving the
24
+ * prevailing flow, and a solid post standing in it; the deficit and the turning
25
+ * behind the post are the incompressible projection's answer, and the banner
26
+ * reads them through exactly the `ClothFluidWind` and `cloth_sample_air` a game
27
+ * scene would use.
28
+ * That is §10's whole argument — this engine already has something better than a
29
+ * wind zone — put where it can be watched instead of read.
30
+ *
31
+ * Three things to watch rather than read, all of them measured in
32
+ * `MEASUREMENTS.md` §49:
33
+ *
34
+ * - **Turn the flow to 90° on "ambient only".** The banner goes limp at the
35
+ * same wind speed — the mean drag acceleration falls from 43 m/s² to 0.08,
36
+ * and the mean facing factor from 0.22 to 0.000. That is §10's normal-facing
37
+ * factor, and the "drag accel" readout is it as a number.
38
+ * - **Do the same on the fluid field, and it does not go limp.** The factor
39
+ * reads 0.22 at 0° and 0.28 at 90°. A wake has velocity in every direction,
40
+ * so there is no orientation of a banner that is edge-on to one — which is
41
+ * something the simulation knows and a wind zone could not have told it.
42
+ * - **Take the post away.** The air at the banner goes from 6.0 m/s to 8.7:
43
+ * the post's deficit is a third of the free stream, and it is the
44
+ * incompressible projection's answer rather than an authored falloff. What
45
+ * the wake is *not*, at the grid a sixty-hertz page can afford, is a
46
+ * shedding street — it is a steady deficit, and the free corner moves twelve
47
+ * times as far per step with the post as without rather than fluttering.
48
+ *
49
+ * And **"ambient only"** is §10's fallback: a constant plus a band-limited
50
+ * gust, uniform over the whole banner, read through the same interface. It
51
+ * costs one sample for the whole cloth instead of one per particle, which the
52
+ * readout shows as the sample time dropping by a factor of twenty-five.
53
+ *
54
+ * @author Alex Goldring
55
+ * @copyright Company Named Limited (c) 2026
56
+ */
57
+
58
+ /** Display rate the fixed step is driven at. */
59
+ const FIXED_STEP = 1 / 60;
60
+
61
+ /** §17's target, for the budget bar. */
62
+ const BUDGET_MS = 2;
63
+
64
+ /** How many recent timings the readout averages. */
65
+ const TIMING_WINDOW = 90;
66
+
67
+ const element = (id) => document.getElementById(id);
68
+
69
+ const canvas = element('view');
70
+ const context = canvas.getContext('2d');
71
+
72
+ const camera = new PlaygroundCamera();
73
+
74
+ camera.target[1] = -0.35;
75
+ camera.distance = 2.6;
76
+ camera.yaw = 0.9;
77
+
78
+ /** The cloth's frame is the world's on this page. */
79
+ const FRAME_ORIGIN = new Float64Array(3);
80
+ const FRAME_ROTATION = new Float64Array([0, 0, 0, 1]);
81
+
82
+ /**
83
+ * Everything the page can change. `size`, `spacing` and `resolution` rebuild;
84
+ * everything else is read afresh every step.
85
+ */
86
+ const control = {
87
+ fabric: 'SILK',
88
+ size: 16,
89
+ spacing: 0.045,
90
+ drag: CLOTH_DYNAMICS_BY_NAME.SILK.drag,
91
+ substeps: 2,
92
+ iterations: 4,
93
+ speed: 9,
94
+ yaw: 0,
95
+ gust: 1.5,
96
+ source: 'field',
97
+ post: true,
98
+ resolution: 18,
99
+ running: true,
100
+ show_field: true
101
+ };
102
+
103
+ /** @type {{state: ClothState, triangle: Int32Array}} */
104
+ let cloth = null;
105
+
106
+ /** @type {ReturnType<build_wind_tunnel>} */
107
+ let tunnel = null;
108
+
109
+ /** @type {ClothFluidWind} */
110
+ const wind = new ClothFluidWind();
111
+
112
+ /** The dataset the wind source reads its fields out of — one component in it. */
113
+ const ecd = new EntityComponentDataset();
114
+
115
+ ecd.setComponentTypeMap([FluidComponent]);
116
+
117
+ let fluid_entity = -1;
118
+
119
+ let time = 0;
120
+
121
+ const cloth_timings = [];
122
+ const fluid_timings = [];
123
+ const sample_timings = [];
124
+
125
+ const air_centre = new Float64Array(3);
126
+
127
+ let sampled_per_particle = false;
128
+ let mean_drag = 0;
129
+ let tripwire = "";
130
+
131
+ // ------------------------------------------------------------------- build
132
+
133
+ function rebuild() {
134
+ const dynamics = CLOTH_DYNAMICS_BY_NAME[control.fabric];
135
+
136
+ cloth = build_cloth_playground_scene({
137
+ shape: 'sheet',
138
+ width: control.size,
139
+ height: control.size,
140
+ spacing: control.spacing,
141
+ mass: 0.4,
142
+ // The fabric row is hard, per §6 — a stretch stiffness an author could
143
+ // reach for is on the wrong side of the dissipative band.
144
+ stretch_stiffness: Infinity,
145
+ bend_modulus: cloth_bend_ratio(dynamics.bend) * 2e-5,
146
+ pinned: 'top',
147
+ flat: false
148
+ });
149
+
150
+ time = 0;
151
+
152
+ cloth_timings.length = 0;
153
+ fluid_timings.length = 0;
154
+ sample_timings.length = 0;
155
+
156
+ rebuild_tunnel();
157
+ }
158
+
159
+ function rebuild_tunnel() {
160
+ const cells = control.resolution;
161
+
162
+ // A volume that holds the banner with room upstream for a wake to form in.
163
+ const cell_size = 0.08;
164
+
165
+ const span_x = cells * cell_size;
166
+ const span_y = cells * cell_size;
167
+ const span_z = Math.round(cells * 1.6) * cell_size;
168
+
169
+ tunnel = build_wind_tunnel({
170
+ cell_size,
171
+ resolution: [cells, cells, Math.round(cells * 1.6)],
172
+ origin: [-span_x * 0.5, -span_y * 0.72, -span_z * 0.5]
173
+ });
174
+
175
+ if (fluid_entity >= 0) {
176
+ ecd.removeEntity(fluid_entity);
177
+ }
178
+
179
+ fluid_entity = ecd.createEntity();
180
+
181
+ ecd.addComponentToEntity(fluid_entity, tunnel.component);
182
+
183
+ refresh_post();
184
+ }
185
+
186
+ function refresh_post() {
187
+ const angle = control.yaw * Math.PI / 180;
188
+
189
+ // Upstream of the banner, along whichever way the air is blowing.
190
+ const distance = 0.55;
191
+
192
+ place_wind_post(tunnel, {
193
+ present: control.post,
194
+ x: -Math.sin(angle) * distance,
195
+ z: -Math.cos(angle) * distance,
196
+ radius: 0.12
197
+ });
198
+ }
199
+
200
+ // -------------------------------------------------------------------- step
201
+
202
+ function step_once() {
203
+ const dynamics = CLOTH_DYNAMICS_BY_NAME[control.fabric];
204
+
205
+ time += FIXED_STEP;
206
+
207
+ const angle = control.yaw * Math.PI / 180;
208
+
209
+ tunnel.effector.wind[0] = Math.sin(angle) * control.speed;
210
+ tunnel.effector.wind[1] = 0;
211
+ tunnel.effector.wind[2] = Math.cos(angle) * control.speed;
212
+
213
+ wind.ambient.wind[0] = tunnel.effector.wind[0];
214
+ wind.ambient.wind[1] = 0;
215
+ wind.ambient.wind[2] = tunnel.effector.wind[2];
216
+
217
+ wind.ambient.gust = control.gust;
218
+ wind.ambient.period = 1.6;
219
+
220
+ // 1. The air. A real fluid step, with the post's wake in it.
221
+ const fluid_started = performance.now();
222
+
223
+ fluid_build_world_to_grid(tunnel.world_to_grid, tunnel.component);
224
+
225
+ tunnel.component.enabled = control.source === 'field';
226
+
227
+ if (tunnel.component.enabled) {
228
+ tunnel.simulator.step(
229
+ tunnel.component.field, FIXED_STEP, [tunnel.effector], tunnel.world_to_grid
230
+ );
231
+ }
232
+
233
+ record(fluid_timings, performance.now() - fluid_started);
234
+
235
+ // 2. The cloth reads it. `begin` once a step, then one sample per particle
236
+ // — or one for the whole banner where no field reaches, which is what
237
+ // "ambient only" turns this page into.
238
+ const sample_started = performance.now();
239
+
240
+ wind.begin(time, ecd);
241
+
242
+ sampled_per_particle = cloth_sample_air(
243
+ cloth.state, wind, FRAME_ORIGIN, FRAME_ROTATION, air_centre
244
+ );
245
+
246
+ record(sample_timings, performance.now() - sample_started);
247
+
248
+ // 3. The step, with §10's term in it.
249
+ const started = performance.now();
250
+
251
+ const response = cloth_drag_response(control.drag, FIXED_STEP);
252
+
253
+ if (response !== 0) {
254
+ cloth_wind_accelerate(cloth.state, response, 0, -9.81, 0);
255
+
256
+ cloth_step(
257
+ cloth.state, FIXED_STEP, 0, -9.81, 0,
258
+ control.substeps, control.iterations,
259
+ null, 0, undefined, cloth.state.external_acceleration
260
+ );
261
+ } else {
262
+ cloth_step(
263
+ cloth.state, FIXED_STEP, 0, -9.81, 0, control.substeps, control.iterations
264
+ );
265
+ }
266
+
267
+ record(cloth_timings, performance.now() - started);
268
+
269
+ tripwire = find_non_finite_cloth_state(cloth.state);
270
+
271
+ measure_drag(response);
272
+ }
273
+
274
+ /**
275
+ * The mean aerodynamic acceleration the banner is actually feeling — the
276
+ * external acceleration with gravity taken back out. It is the normal-facing
277
+ * factor made into a number: turn the flow ninety degrees at a fixed wind speed
278
+ * and watch it collapse.
279
+ *
280
+ * @param {number} response
281
+ */
282
+ function measure_drag(response) {
283
+ if (response === 0) {
284
+ mean_drag = 0;
285
+
286
+ return;
287
+ }
288
+
289
+ const state = cloth.state;
290
+
291
+ let total = 0;
292
+ let free = 0;
293
+
294
+ for (let i = 0; i < state.particle_count; i++) {
295
+ if (state.mass_inverse[i] === 0) {
296
+ continue;
297
+ }
298
+
299
+ const p = i * 3;
300
+
301
+ total += Math.hypot(
302
+ state.external_acceleration[p],
303
+ state.external_acceleration[p + 1] + 9.81,
304
+ state.external_acceleration[p + 2]
305
+ );
306
+
307
+ free++;
308
+ }
309
+
310
+ mean_drag = free === 0 ? 0 : total / free;
311
+ }
312
+
313
+ /**
314
+ * @param {number[]} window
315
+ * @param {number} value
316
+ */
317
+ function record(window, value) {
318
+ window.push(value);
319
+
320
+ if (window.length > TIMING_WINDOW) {
321
+ window.shift();
322
+ }
323
+ }
324
+
325
+ /**
326
+ * @param {number[]} window
327
+ * @returns {number}
328
+ */
329
+ function mean(window) {
330
+ if (window.length === 0) {
331
+ return 0;
332
+ }
333
+
334
+ let total = 0;
335
+
336
+ for (const value of window) {
337
+ total += value;
338
+ }
339
+
340
+ return total / window.length;
341
+ }
342
+
343
+ // -------------------------------------------------------------------- draw
344
+
345
+ /**
346
+ * The flow itself, on a horizontal slice through the banner's waist: one
347
+ * segment per cell, along the air, brighter where it is faster. The wake is the
348
+ * part that is not pointing downstream.
349
+ */
350
+ function draw_field() {
351
+ if (!control.show_field || control.source !== 'field') {
352
+ return;
353
+ }
354
+
355
+ const component = tunnel.component;
356
+
357
+ const [res_x, res_y, res_z] = tunnel.resolution;
358
+
359
+ const cell_size = tunnel.cell_size;
360
+
361
+ const width = context.canvas.width;
362
+ const height = context.canvas.height;
363
+
364
+ const from = new Float64Array(2);
365
+ const to = new Float64Array(2);
366
+
367
+ const sample = new Float64Array(3);
368
+
369
+ const slice = Math.floor(res_y * 0.55);
370
+
371
+ const stride = res_x > 24 ? 2 : 1;
372
+
373
+ context.lineWidth = Math.max(1, width / 900);
374
+
375
+ for (let k = 0; k < res_z; k += stride) {
376
+ for (let i = 0; i < res_x; i += stride) {
377
+ const x = component.origin[0] + i * cell_size;
378
+ const y = component.origin[1] + slice * cell_size;
379
+ const z = component.origin[2] + k * cell_size;
380
+
381
+ if (component.field.solid[k * res_x * res_y + slice * res_x + i] !== 0) {
382
+ const depth = project(from, camera, x, y, z, width, height);
383
+
384
+ if (depth > 0) {
385
+ context.fillStyle = '#e5533d';
386
+ context.fillRect(from[0] - 2, from[1] - 2, 4, 4);
387
+ }
388
+
389
+ continue;
390
+ }
391
+
392
+ component.sampleVelocityAtWorld(sample, x, y, z);
393
+
394
+ // Cells a second to metres a second, the same conversion
395
+ // `ClothFluidWind` makes and for the same reason.
396
+ const vx = sample[0] * cell_size;
397
+ const vz = sample[2] * cell_size;
398
+
399
+ const speed = Math.hypot(vx, sample[1] * cell_size, vz);
400
+
401
+ if (speed < 1e-3) {
402
+ continue;
403
+ }
404
+
405
+ const scale = 0.045 / Math.max(speed, 1e-3);
406
+
407
+ const depth = project(from, camera, x, y, z, width, height);
408
+
409
+ project(to, camera, x + vx * scale, y + sample[1] * cell_size * scale, z + vz * scale, width, height);
410
+
411
+ if (depth <= 0) {
412
+ continue;
413
+ }
414
+
415
+ const t = Math.min(1, speed / Math.max(control.speed, 0.01));
416
+
417
+ context.strokeStyle = `rgba(${Math.round(80 + 140 * t)}, ${Math.round(150 + 60 * t)}, 220, ${0.25 + 0.5 * t})`;
418
+
419
+ context.beginPath();
420
+ context.moveTo(from[0], from[1]);
421
+ context.lineTo(to[0], to[1]);
422
+ context.stroke();
423
+ }
424
+ }
425
+ }
426
+
427
+ function refresh_readout() {
428
+ const cloth_ms = mean(cloth_timings);
429
+
430
+ element('ms').textContent = cloth_ms.toFixed(3);
431
+ element('fluid_ms').textContent = mean(fluid_timings).toFixed(3);
432
+ element('sample_us').textContent = (mean(sample_timings) * 1000).toFixed(1);
433
+
434
+ element('sample_mode').textContent = sampled_per_particle
435
+ ? `per particle (${cloth.state.particle_count})`
436
+ : 'once for the cloth';
437
+
438
+ element('counts').textContent = `${cloth.state.particle_count} / ${cloth.state.stretch_count}`;
439
+
440
+ element('air').textContent =
441
+ `${Math.hypot(air_centre[0], air_centre[1], air_centre[2]).toFixed(2)} m/s`;
442
+
443
+ element('drag_accel').textContent = `${mean_drag.toFixed(2)} m/s²`;
444
+
445
+ const tripwire_element = element('tripwire');
446
+
447
+ tripwire_element.textContent = tripwire === "" ? 'ok' : tripwire;
448
+ tripwire_element.className = tripwire === "" ? 'good' : 'bad';
449
+
450
+ const budget = element('budget');
451
+
452
+ budget.classList.toggle('over', cloth_ms > BUDGET_MS);
453
+ budget.firstElementChild.style.width = `${Math.min(100, 100 * cloth_ms / BUDGET_MS)}%`;
454
+ }
455
+
456
+ function frame() {
457
+ if (control.running) {
458
+ step_once();
459
+ }
460
+
461
+ draw_cloth(context, camera, cloth.state, cloth.triangle, {
462
+ view: 'shaded',
463
+ colour_of_particle: null,
464
+ grabbed: -1
465
+ });
466
+
467
+ draw_field();
468
+
469
+ refresh_readout();
470
+
471
+ requestAnimationFrame(frame);
472
+ }
473
+
474
+ // ---------------------------------------------------------------- controls
475
+
476
+ const fabric_select = element('fabric');
477
+
478
+ for (const name of Object.keys(CLOTH_DYNAMICS_BY_NAME)) {
479
+ const option = document.createElement('option');
480
+
481
+ option.value = name;
482
+ option.textContent = name.toLowerCase();
483
+
484
+ fabric_select.append(option);
485
+ }
486
+
487
+ fabric_select.value = control.fabric;
488
+
489
+ fabric_select.addEventListener('change', () => {
490
+ control.fabric = fabric_select.value;
491
+ control.drag = CLOTH_DYNAMICS_BY_NAME[control.fabric].drag;
492
+
493
+ refresh_inputs();
494
+ rebuild();
495
+ });
496
+
497
+ /**
498
+ * @param {string} id
499
+ * @param {string} key
500
+ * @param {function(number): string} format
501
+ * @param {boolean} [rebuilds]
502
+ */
503
+ function slider(id, key, format, rebuilds = false) {
504
+ const input = element(id);
505
+ const output = element(`${id}_out`);
506
+
507
+ input.value = String(control[key]);
508
+ output.textContent = format(control[key]);
509
+
510
+ input.addEventListener('input', () => {
511
+ control[key] = Number(input.value);
512
+
513
+ output.textContent = format(control[key]);
514
+
515
+ if (rebuilds) {
516
+ rebuild();
517
+ } else if (key === 'yaw') {
518
+ refresh_post();
519
+ }
520
+ });
521
+
522
+ return { input, output, format };
523
+ }
524
+
525
+ const SLIDERS = [
526
+ slider('size', 'size', v => `${v}²`, true),
527
+ slider('spacing', 'spacing', v => `${(v * 100).toFixed(1)} cm`, true),
528
+ slider('drag', 'drag', v => v.toFixed(2)),
529
+ slider('substeps', 'substeps', v => String(v)),
530
+ slider('iterations', 'iterations', v => String(v)),
531
+ slider('speed', 'speed', v => `${v.toFixed(1)} m/s`),
532
+ slider('yaw', 'yaw', v => `${v}°`),
533
+ slider('gust', 'gust', v => `${v.toFixed(1)} m/s`),
534
+ slider('resolution', 'resolution', v => `${v}³-ish`, true)
535
+ ];
536
+
537
+ function refresh_inputs() {
538
+ for (const entry of SLIDERS) {
539
+ const key = entry.input.id;
540
+
541
+ entry.input.value = String(control[key]);
542
+ entry.output.textContent = entry.format(control[key]);
543
+ }
544
+ }
545
+
546
+ const source_select = element('source');
547
+
548
+ source_select.value = control.source;
549
+
550
+ source_select.addEventListener('change', () => {
551
+ control.source = source_select.value;
552
+ });
553
+
554
+ const post_button = element('post');
555
+
556
+ post_button.classList.toggle('on', control.post);
557
+
558
+ post_button.addEventListener('click', () => {
559
+ control.post = !control.post;
560
+
561
+ post_button.classList.toggle('on', control.post);
562
+
563
+ refresh_post();
564
+ });
565
+
566
+ const field_button = element('show_field');
567
+
568
+ field_button.classList.toggle('on', control.show_field);
569
+
570
+ field_button.addEventListener('click', () => {
571
+ control.show_field = !control.show_field;
572
+
573
+ field_button.classList.toggle('on', control.show_field);
574
+ });
575
+
576
+ element('pause').addEventListener('click', (event) => {
577
+ control.running = !control.running;
578
+
579
+ event.currentTarget.textContent = control.running ? 'pause' : 'resume';
580
+ });
581
+
582
+ element('single').addEventListener('click', step_once);
583
+ element('reset').addEventListener('click', rebuild);
584
+
585
+ // ------------------------------------------------------------------ camera
586
+
587
+ let drag = null;
588
+
589
+ canvas.addEventListener('pointerdown', (event) => {
590
+ canvas.setPointerCapture(event.pointerId);
591
+ canvas.classList.add('grabbing');
592
+
593
+ drag = {
594
+ x: event.clientX,
595
+ y: event.clientY,
596
+ pan: event.shiftKey || event.button === 2
597
+ };
598
+ });
599
+
600
+ canvas.addEventListener('pointermove', (event) => {
601
+ if (drag === null) {
602
+ return;
603
+ }
604
+
605
+ const dx = event.clientX - drag.x;
606
+ const dy = event.clientY - drag.y;
607
+
608
+ drag.x = event.clientX;
609
+ drag.y = event.clientY;
610
+
611
+ if (!drag.pan) {
612
+ camera.yaw -= dx * 0.006;
613
+ camera.pitch = Math.max(-1.4, Math.min(1.4, camera.pitch + dy * 0.006));
614
+
615
+ return;
616
+ }
617
+
618
+ const scale = camera.distance * 0.0016;
619
+
620
+ camera.target[0] -= dx * Math.cos(camera.yaw) * scale;
621
+ camera.target[2] += dx * Math.sin(camera.yaw) * scale;
622
+ camera.target[1] += dy * scale;
623
+ });
624
+
625
+ function release(event) {
626
+ drag = null;
627
+
628
+ canvas.classList.remove('grabbing');
629
+
630
+ if (event !== undefined && canvas.hasPointerCapture(event.pointerId)) {
631
+ canvas.releasePointerCapture(event.pointerId);
632
+ }
633
+ }
634
+
635
+ canvas.addEventListener('pointerup', release);
636
+ canvas.addEventListener('pointercancel', release);
637
+ canvas.addEventListener('contextmenu', (event) => event.preventDefault());
638
+
639
+ canvas.addEventListener('wheel', (event) => {
640
+ event.preventDefault();
641
+
642
+ camera.distance = Math.max(0.2, Math.min(30, camera.distance * Math.exp(event.deltaY * 0.0012)));
643
+ }, { passive: false });
644
+
645
+ // ------------------------------------------------------------------- start
646
+
647
+ function resize() {
648
+ const ratio = Math.min(2, window.devicePixelRatio || 1);
649
+
650
+ canvas.width = Math.round(canvas.clientWidth * ratio);
651
+ canvas.height = Math.round(canvas.clientHeight * ratio);
652
+ }
653
+
654
+ window.addEventListener('resize', resize);
655
+
656
+ resize();
657
+ rebuild();
658
+
659
+ requestAnimationFrame(frame);
660
+
661
+ // A handle for a driver script — `requestAnimationFrame` does not fire while the
662
+ // pane is hidden, so a driver steps the page itself.
663
+ globalThis.cloth_wind_playground = {
664
+ control,
665
+ camera,
666
+ rebuild,
667
+ step_once,
668
+ refresh_post,
669
+ get cloth() { return cloth; },
670
+ get tunnel() { return tunnel; },
671
+ get wind() { return wind; },
672
+ get air_centre() { return air_centre; },
673
+ get sampled_per_particle() { return sampled_per_particle; },
674
+ get mean_drag() { return mean_drag; },
675
+ get tripwire() { return tripwire; },
676
+ timings: {
677
+ get cloth() { return mean(cloth_timings); },
678
+ get fluid() { return mean(fluid_timings); },
679
+ get sample() { return mean(sample_timings); }
680
+ }
681
+ };