@woosh/meep-engine 3.56.0 → 3.57.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 (625) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/DUPLICATION_REVIEW_2026_10_24.md +1072 -0
  4. package/src/core/binary/BitSet.js +1 -1
  5. package/src/core/binary/FLOAT16_MAX.d.ts +2 -0
  6. package/src/core/binary/FLOAT16_MAX.d.ts.map +1 -0
  7. package/src/core/binary/FLOAT16_MAX.js +1 -0
  8. package/src/core/binary/base58/base58_encode.js +2 -2
  9. package/src/core/binary/type/TypeBuilder.js +1 -1
  10. package/src/core/bvh2/bvh3/BVH.js +1 -1
  11. package/src/core/bvh2/bvh3/ebvh_optimize_treelet.d.ts.map +1 -1
  12. package/src/core/bvh2/bvh3/ebvh_optimize_treelet.js +7 -3
  13. package/src/core/bvh2/bvh3/ebvh_update_hierarchy_bounds.d.ts.map +1 -1
  14. package/src/core/bvh2/bvh3/ebvh_update_hierarchy_bounds.js +5 -1
  15. package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.d.ts.map +1 -1
  16. package/src/core/bvh2/bvh3/query/compute_tight_near_far_clipping_planes.js +195 -246
  17. package/src/core/collection/CuckooFilter.js +1 -1
  18. package/src/core/collection/array/isArrayEqual.d.ts.map +1 -1
  19. package/src/core/collection/array/isArrayEqual.js +33 -58
  20. package/src/core/collection/array/typed/array_buffer_hash.d.ts.map +1 -1
  21. package/src/core/collection/array/typed/array_buffer_hash.js +41 -39
  22. package/src/core/collection/array/typed/float32_array_hash.js +38 -38
  23. package/src/core/collection/array/typed/uint16_array_hash.d.ts +2 -1
  24. package/src/core/collection/array/typed/uint16_array_hash.d.ts.map +1 -1
  25. package/src/core/collection/array/typed/uint16_array_hash.js +30 -29
  26. package/src/core/collection/array/typed/uint32_array_hash.d.ts +2 -1
  27. package/src/core/collection/array/typed/uint32_array_hash.d.ts.map +1 -1
  28. package/src/core/collection/array/typed/uint32_array_hash.js +21 -20
  29. package/src/core/collection/array/typed/uint8_array_hash.d.ts +2 -1
  30. package/src/core/collection/array/typed/uint8_array_hash.d.ts.map +1 -1
  31. package/src/core/collection/array/typed/uint8_array_hash.js +37 -36
  32. package/src/core/collection/array/typed/uint_array_for_count.js +2 -2
  33. package/src/core/collection/list/List.d.ts.map +1 -1
  34. package/src/core/collection/list/List.js +3 -19
  35. package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
  36. package/src/core/collection/table/RowFirstTable.js +4 -28
  37. package/src/core/debug/matchers/DescribeAs.d.ts.map +1 -1
  38. package/src/core/debug/matchers/DescribeAs.js +62 -64
  39. package/src/core/function/FunctionCompiler.js +4 -4
  40. package/src/core/function/objectsEqual.d.ts.map +1 -1
  41. package/src/core/function/objectsEqual.js +3 -2
  42. package/src/core/geom/2d/bvh/BVH2D.d.ts.map +1 -1
  43. package/src/core/geom/2d/bvh/BVH2D.js +4 -3
  44. package/src/core/geom/2d/convex-hull/convex_hull_fixed_count_2d_relaxation.d.ts +5 -2
  45. package/src/core/geom/2d/convex-hull/convex_hull_fixed_count_2d_relaxation.d.ts.map +1 -1
  46. package/src/core/geom/2d/convex-hull/convex_hull_fixed_count_2d_relaxation.js +49 -11
  47. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.d.ts.map +1 -1
  48. package/src/core/geom/2d/hash-grid/shg_query_elements_circle.js +122 -159
  49. package/src/core/geom/2d/hash-grid/shg_query_elements_line.d.ts.map +1 -1
  50. package/src/core/geom/2d/hash-grid/shg_query_elements_line.js +8 -5
  51. package/src/core/geom/2d/polygon/polygon2_rasterize_coverage.d.ts +18 -0
  52. package/src/core/geom/2d/polygon/polygon2_rasterize_coverage.d.ts.map +1 -0
  53. package/src/core/geom/2d/polygon/polygon2_rasterize_coverage.js +97 -0
  54. package/src/core/geom/2d/r-tree/StaticR2Tree.js +2 -2
  55. package/src/core/geom/2d/triangle/tri2_rasterize_coverage.d.ts +21 -0
  56. package/src/core/geom/2d/triangle/tri2_rasterize_coverage.d.ts.map +1 -0
  57. package/src/core/geom/2d/triangle/tri2_rasterize_coverage.js +54 -0
  58. package/src/core/geom/3d/aabb/aabb3_build_frustum.d.ts.map +1 -1
  59. package/src/core/geom/3d/aabb/aabb3_build_frustum.js +20 -53
  60. package/src/core/geom/3d/aabb/aabb3_score_boxes_SAH.d.ts +1 -1
  61. package/src/core/geom/3d/aabb/aabb3_score_boxes_SAH.d.ts.map +1 -1
  62. package/src/core/geom/3d/aabb/aabb3_score_boxes_SAH.js +11 -42
  63. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.d.ts.map +1 -1
  64. package/src/core/geom/3d/atlas/pack/atlas_pack_charts.js +30 -32
  65. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.d.ts.map +1 -1
  66. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_bff.js +1 -5
  67. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.d.ts.map +1 -1
  68. package/src/core/geom/3d/atlas/param/atlas_chart_parameterize_lscm.js +1 -15
  69. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.d.ts.map +1 -1
  70. package/src/core/geom/3d/atlas/segment/atlas_merge_charts.js +105 -116
  71. package/src/core/geom/3d/line/line3_compute_segment_point_distance_sqr.d.ts.map +1 -1
  72. package/src/core/geom/3d/line/line3_compute_segment_point_distance_sqr.js +36 -54
  73. package/src/core/geom/3d/plane/plane3_compute_intersection_objects.d.ts +5 -10
  74. package/src/core/geom/3d/plane/plane3_compute_intersection_objects.d.ts.map +1 -1
  75. package/src/core/geom/3d/plane/plane3_compute_intersection_objects.js +20 -82
  76. package/src/core/geom/3d/plane/plane3_slerp.d.ts.map +1 -1
  77. package/src/core/geom/3d/plane/plane3_slerp.js +87 -102
  78. package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts +12 -0
  79. package/src/core/geom/3d/polygon/polygon3_compute_normal.d.ts.map +1 -1
  80. package/src/core/geom/3d/polygon/polygon3_compute_normal.js +107 -60
  81. package/src/core/geom/3d/ray/ray3_compute_closest_point.d.ts.map +1 -1
  82. package/src/core/geom/3d/ray/ray3_compute_closest_point.js +37 -43
  83. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  84. package/src/core/geom/3d/shape/ConvexHullShape3D.js +5 -16
  85. package/src/core/geom/3d/shape/MeshShape3D.js +2 -56
  86. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  87. package/src/core/geom/3d/shape/TransformedShape3D.js +10 -7
  88. package/src/core/geom/3d/shape/UnionShape3D.d.ts.map +1 -1
  89. package/src/core/geom/3d/shape/UnionShape3D.js +9 -3
  90. package/src/core/geom/3d/shape/json/shape_to_type.js +2 -2
  91. package/src/core/geom/3d/sphere/harmonics/SH3_COEFFICIENTS.js +17 -17
  92. package/src/core/geom/3d/tetrahedra/TetrahedralMesh.d.ts.map +1 -1
  93. package/src/core/geom/3d/tetrahedra/TetrahedralMesh.js +6 -2
  94. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.d.ts.map +1 -1
  95. package/src/core/geom/3d/tetrahedra/tetrahedral_mesh_carve_outside_surface.js +3 -2
  96. package/src/core/geom/3d/tetrahedra/tetrahedron_contains_point.d.ts +20 -2
  97. package/src/core/geom/3d/tetrahedra/tetrahedron_contains_point.d.ts.map +1 -1
  98. package/src/core/geom/3d/tetrahedra/tetrahedron_contains_point.js +52 -37
  99. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.d.ts.map +1 -1
  100. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_face_normals.js +40 -56
  101. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
  102. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +251 -260
  103. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts.map +1 -1
  104. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.js +104 -113
  105. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.d.ts.map +1 -1
  106. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_resolve_t_junctions.js +227 -238
  107. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  108. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +15 -24
  109. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.d.ts.map +1 -1
  110. package/src/core/geom/3d/topology/struct/binary/io/edge/bt_edge_create.js +31 -115
  111. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.d.ts.map +1 -1
  112. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_fuse_duplicate_edges.js +142 -150
  113. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.d.ts.map +1 -1
  114. package/src/core/geom/3d/topology/struct/binary/io/vertex/bt_vertex_replace.js +151 -194
  115. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_incenter.d.ts.map +1 -1
  116. package/src/core/geom/3d/topology/struct/binary/query/bt_face_get_incenter.js +60 -85
  117. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_distance_field.d.ts.map +1 -1
  118. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_boundary_distance_field.js +156 -171
  119. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.d.ts.map +1 -1
  120. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path.js +8 -44
  121. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.d.ts.map +1 -1
  122. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_face_find_path_polyanya.js +2 -7
  123. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.d.ts.map +1 -1
  124. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_sample_interior_grid_points.js +3 -2
  125. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.d.ts.map +1 -1
  126. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_segment_penetrates_surface.js +128 -133
  127. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.d.ts +8 -14
  128. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.d.ts.map +1 -1
  129. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_surface_ray_parity.js +36 -82
  130. package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts.map +1 -1
  131. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +7 -144
  132. package/src/core/geom/ConicRay.d.ts.map +1 -1
  133. package/src/core/geom/ConicRay.js +230 -237
  134. package/src/core/localization/LocaleDataset.d.ts +6 -0
  135. package/src/core/localization/LocaleDataset.d.ts.map +1 -1
  136. package/src/core/localization/LocaleDataset.js +48 -37
  137. package/src/core/localization/LocalizationEngine.d.ts.map +1 -1
  138. package/src/core/localization/LocalizationEngine.js +3 -2
  139. package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
  140. package/src/core/math/lookup/ParameterLookupTable.js +3 -1
  141. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.d.ts.map +1 -1
  142. package/src/core/math/spline/spline3_hermite_intersection_spline3_hermite_nd.js +330 -400
  143. package/src/core/math/spline/spline3_hermite_nearest_point.d.ts +16 -0
  144. package/src/core/math/spline/spline3_hermite_nearest_point.d.ts.map +1 -1
  145. package/src/core/math/spline/spline3_hermite_nearest_point.js +134 -167
  146. package/src/core/model/DebouncedObservedBoolean.d.ts +1 -2
  147. package/src/core/model/DebouncedObservedBoolean.d.ts.map +1 -1
  148. package/src/core/model/DebouncedObservedBoolean.js +1 -1
  149. package/src/core/model/node-graph/node/NodeInstance.d.ts.map +1 -1
  150. package/src/core/model/node-graph/node/NodeInstance.js +2 -7
  151. package/src/core/model/node-graph/node/parameter/NodeParameterDescription.js +1 -1
  152. package/src/core/model/object/optional_equality.d.ts +3 -3
  153. package/src/core/model/object/optional_equality.d.ts.map +1 -1
  154. package/src/core/model/object/optional_equality.js +10 -27
  155. package/src/core/model/object/write_property.js +1 -1
  156. package/src/core/model/reactive/evaluation/MultiPredicateEvaluator.js +2 -2
  157. package/src/core/primitives/strings/string_compute_byte_size.d.ts +3 -3
  158. package/src/core/primitives/strings/string_compute_byte_size.d.ts.map +1 -1
  159. package/src/core/primitives/strings/string_compute_byte_size.js +8 -21
  160. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  161. package/src/core/process/executor/ConcurrentExecutor.js +14 -2
  162. package/src/engine/achievements/gateway/StorageAchievementGateway.d.ts +6 -3
  163. package/src/engine/achievements/gateway/StorageAchievementGateway.d.ts.map +1 -1
  164. package/src/engine/achievements/gateway/StorageAchievementGateway.js +31 -75
  165. package/src/engine/asset/loaders/SVGAssetLoader.d.ts.map +1 -1
  166. package/src/engine/asset/loaders/SVGAssetLoader.js +33 -31
  167. package/src/engine/control/first-person/math/criticallyDampedSpring.d.ts.map +1 -1
  168. package/src/engine/control/first-person/math/criticallyDampedSpring.js +3 -14
  169. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  170. package/src/engine/ecs/EntityComponentDataset.js +4 -1
  171. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.d.ts +0 -2
  172. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.d.ts.map +1 -1
  173. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter.js +3 -3
  174. package/src/engine/ecs/terrain/ecs/layers/TerrainLayer.js +2 -2
  175. package/src/engine/graphics/sh3/path_tracer/geometry/sort_triangles_by_distance_to_point.js +1 -1
  176. package/src/engine/graphics/texture/sampler/sampler2d_channel_compute_min_indices.js +1 -1
  177. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  178. package/src/engine/graphics3/WaterSystem.js +2 -1
  179. package/src/engine/graphics3/prefab/prefab_skin_frame.d.ts.map +1 -1
  180. package/src/engine/graphics3/prefab/prefab_skin_frame.js +1 -12
  181. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts +1 -1
  182. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
  183. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +141 -137
  184. package/src/engine/input/ecs/systems/InputControllerSystem.d.ts.map +1 -1
  185. package/src/engine/input/ecs/systems/InputControllerSystem.js +13 -3
  186. package/src/engine/input/ecs/systems/InputSystem.d.ts.map +1 -1
  187. package/src/engine/input/ecs/systems/InputSystem.js +2 -1
  188. package/src/engine/intelligence/behavior/composite/ParallelBehavior.d.ts +1 -1
  189. package/src/engine/intelligence/behavior/composite/ParallelBehavior.d.ts.map +1 -1
  190. package/src/engine/intelligence/behavior/composite/ParallelBehavior.js +4 -1
  191. package/src/engine/intelligence/behavior/decorator/InvertStatusBehavior.d.ts.map +1 -1
  192. package/src/engine/intelligence/behavior/decorator/InvertStatusBehavior.js +3 -1
  193. package/src/engine/intelligence/behavior/decorator/RepeatBehavior.d.ts.map +1 -1
  194. package/src/engine/intelligence/behavior/decorator/RepeatBehavior.js +3 -1
  195. package/src/engine/intelligence/behavior/decorator/RepeatBehaviorSerializationAdapter.d.ts.map +1 -1
  196. package/src/engine/intelligence/behavior/decorator/RepeatBehaviorSerializationAdapter.js +39 -33
  197. package/src/engine/intelligence/behavior/util/ConditionalBehavior.d.ts +5 -0
  198. package/src/engine/intelligence/behavior/util/ConditionalBehavior.d.ts.map +1 -1
  199. package/src/engine/intelligence/behavior/util/ConditionalBehavior.js +93 -75
  200. package/src/engine/intelligence/behavior/util/RotationBehavior.d.ts.map +1 -1
  201. package/src/engine/intelligence/behavior/util/RotationBehavior.js +79 -76
  202. package/src/engine/intelligence/blackboard/BlackboardStack.d.ts +1 -0
  203. package/src/engine/intelligence/blackboard/BlackboardStack.d.ts.map +1 -1
  204. package/src/engine/intelligence/blackboard/BlackboardStack.js +159 -123
  205. package/src/engine/intelligence/mcts/StateNode.d.ts.map +1 -1
  206. package/src/engine/intelligence/mcts/StateNode.js +366 -364
  207. package/src/engine/intelligence/resource/ResourceAllocationSolver.js +1 -1
  208. package/src/engine/navigation/ecs/components/Path.d.ts.map +1 -1
  209. package/src/engine/navigation/ecs/components/Path.js +2 -1
  210. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.d.ts.map +1 -1
  211. package/src/engine/navigation/ecs/path_following/PathFollowingSystem.js +5 -2
  212. package/src/engine/physics/body/SolverBodyState.d.ts.map +1 -1
  213. package/src/engine/physics/body/SolverBodyState.js +2 -6
  214. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  215. package/src/engine/physics/ccd/linear_sweep.js +3 -16
  216. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -1
  217. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +2 -16
  218. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +13 -1
  219. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  220. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +15 -4
  221. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  222. package/src/engine/physics/ecs/PhysicsSystem.js +2 -5
  223. package/src/engine/physics/inertia/locked_inverse_inertia.d.ts +12 -0
  224. package/src/engine/physics/inertia/locked_inverse_inertia.d.ts.map +1 -0
  225. package/src/engine/physics/inertia/locked_inverse_inertia.js +22 -0
  226. package/src/engine/physics/integration/integrate_velocity.d.ts.map +1 -1
  227. package/src/engine/physics/integration/integrate_velocity.js +3 -18
  228. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts +1 -0
  229. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
  230. package/src/engine/physics/mls-mpm/MLS_MPM.js +5 -4
  231. package/src/engine/physics/narrowphase/convex_decomposition.d.ts.map +1 -1
  232. package/src/engine/physics/narrowphase/convex_decomposition.js +7 -16
  233. package/src/engine/physics/vehicle/RaycastVehicle.d.ts.map +1 -1
  234. package/src/engine/physics/vehicle/RaycastVehicle.js +15 -5
  235. package/src/engine/save/StorageBackedList.d.ts +5 -0
  236. package/src/engine/save/StorageBackedList.d.ts.map +1 -1
  237. package/src/engine/save/StorageBackedList.js +161 -144
  238. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  239. package/src/engine/simulation/Ticker.js +394 -391
  240. package/src/engine/sound/simulation/probe/AcousticProbeField.d.ts.map +1 -1
  241. package/src/engine/sound/simulation/probe/AcousticProbeField.js +6 -19
  242. package/src/engine/sound/simulation/probe/probe_edge_visible.d.ts +15 -0
  243. package/src/engine/sound/simulation/probe/probe_edge_visible.d.ts.map +1 -1
  244. package/src/engine/sound/simulation/probe/probe_edge_visible.js +59 -41
  245. package/src/engine/sound/simulation/probe/probe_refine_connectivity.d.ts.map +1 -1
  246. package/src/engine/sound/simulation/probe/probe_refine_connectivity.js +164 -181
  247. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.d.ts.map +1 -1
  248. package/src/engine/sound/simulation/render/FoaProbeReverbRenderer.js +20 -5
  249. package/src/format/image/avif/av1/decode/Av1FrameContext.d.ts.map +1 -1
  250. package/src/format/image/avif/av1/decode/Av1FrameContext.js +3 -10
  251. package/src/format/image/avif/av1/decode/reconstruct.d.ts +20 -0
  252. package/src/format/image/avif/av1/decode/reconstruct.d.ts.map +1 -1
  253. package/src/format/image/avif/av1/decode/reconstruct.js +22 -9
  254. package/src/format/image/avif/av1/encode/choose_restoration.js +2 -18
  255. package/src/format/image/avif/av1/encode/quantise.d.ts.map +1 -1
  256. package/src/format/image/avif/av1/encode/quantise.js +1 -55
  257. package/src/format/image/avif/av1/filter/loop_restoration.d.ts +9 -0
  258. package/src/format/image/avif/av1/filter/loop_restoration.d.ts.map +1 -1
  259. package/src/format/image/avif/av1/filter/loop_restoration.js +1 -1
  260. package/src/format/texture/basis/transcode/etc1s_to_bc7.js +2 -33
  261. package/src/format/texture/basis/transcode/fit_endpoints.d.ts +11 -0
  262. package/src/format/texture/basis/transcode/fit_endpoints.d.ts.map +1 -1
  263. package/src/format/texture/basis/transcode/fit_endpoints.js +1 -1
  264. package/src/format/texture/basis/uastc/uastc_decode_block.d.ts.map +1 -1
  265. package/src/format/texture/basis/uastc/uastc_decode_block.js +2 -19
  266. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +220 -0
  267. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +172 -0
  268. package/src/generation/foliage/FOLIAGE_DESIGN.md +165 -15
  269. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +158 -0
  270. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +248 -0
  271. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +694 -0
  272. package/src/generation/foliage/analysis/CrownOpacity.d.ts +118 -0
  273. package/src/generation/foliage/analysis/CrownOpacity.d.ts.map +1 -0
  274. package/src/generation/foliage/analysis/CrownOpacity.js +182 -0
  275. package/src/generation/foliage/analysis/crown_opacity_frames.d.ts +10 -0
  276. package/src/generation/foliage/analysis/crown_opacity_frames.d.ts.map +1 -0
  277. package/src/generation/foliage/analysis/crown_opacity_frames.js +132 -0
  278. package/src/generation/foliage/analysis/crown_opacity_measure.d.ts +51 -0
  279. package/src/generation/foliage/analysis/crown_opacity_measure.d.ts.map +1 -0
  280. package/src/generation/foliage/analysis/crown_opacity_measure.js +649 -0
  281. package/src/generation/foliage/analysis/foliage_measure_leaf_area.d.ts +66 -0
  282. package/src/generation/foliage/analysis/foliage_measure_leaf_area.d.ts.map +1 -0
  283. package/src/generation/foliage/analysis/foliage_measure_leaf_area.js +192 -0
  284. package/src/generation/foliage/analysis/tree_crown_opacity.d.ts +27 -0
  285. package/src/generation/foliage/analysis/tree_crown_opacity.d.ts.map +1 -0
  286. package/src/generation/foliage/analysis/tree_crown_opacity.js +58 -0
  287. package/src/generation/foliage/animation/build_tree_wind_rig.d.ts +3 -2
  288. package/src/generation/foliage/animation/build_tree_wind_rig.d.ts.map +1 -1
  289. package/src/generation/foliage/animation/build_tree_wind_rig.js +12 -30
  290. package/src/generation/foliage/animation/tree_wind_bend_limit.d.ts +12 -0
  291. package/src/generation/foliage/animation/tree_wind_bend_limit.d.ts.map +1 -0
  292. package/src/generation/foliage/animation/tree_wind_bend_limit.js +13 -0
  293. package/src/generation/foliage/asset/TreeAsset.d.ts +5 -0
  294. package/src/generation/foliage/asset/TreeAsset.d.ts.map +1 -1
  295. package/src/generation/foliage/asset/TreeAsset.js +6 -0
  296. package/src/generation/foliage/asset/generate_tree.d.ts.map +1 -1
  297. package/src/generation/foliage/asset/generate_tree.js +23 -6
  298. package/src/generation/foliage/config/{BranchLevelConfig.d.ts → BranchGeneratorConfig.d.ts} +61 -75
  299. package/src/generation/foliage/config/BranchGeneratorConfig.d.ts.map +1 -0
  300. package/src/generation/foliage/config/{BranchLevelConfig.js → BranchGeneratorConfig.js} +83 -126
  301. package/src/generation/foliage/config/BranchSpawnRule.d.ts +118 -0
  302. package/src/generation/foliage/config/BranchSpawnRule.d.ts.map +1 -0
  303. package/src/generation/foliage/config/BranchSpawnRule.js +217 -0
  304. package/src/generation/foliage/config/FoliageCardType.d.ts +1 -1
  305. package/src/generation/foliage/config/FoliageCardType.js +2 -2
  306. package/src/generation/foliage/config/FoliageConfig.d.ts +23 -45
  307. package/src/generation/foliage/config/FoliageConfig.d.ts.map +1 -1
  308. package/src/generation/foliage/config/FoliageConfig.js +47 -93
  309. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts +12 -20
  310. package/src/generation/foliage/config/FoliageMaterialConfig.d.ts.map +1 -1
  311. package/src/generation/foliage/config/FoliageMaterialConfig.js +261 -279
  312. package/src/generation/foliage/config/FoliageSpawnRule.d.ts +77 -0
  313. package/src/generation/foliage/config/FoliageSpawnRule.d.ts.map +1 -0
  314. package/src/generation/foliage/config/FoliageSpawnRule.js +135 -0
  315. package/src/generation/foliage/config/FoliageTileContent.d.ts +9 -0
  316. package/src/generation/foliage/config/FoliageTileContent.d.ts.map +1 -0
  317. package/src/generation/foliage/config/FoliageTileContent.js +12 -0
  318. package/src/generation/foliage/config/LeafApexShape.d.ts +12 -0
  319. package/src/generation/foliage/config/LeafApexShape.d.ts.map +1 -0
  320. package/src/generation/foliage/config/LeafApexShape.js +17 -0
  321. package/src/generation/foliage/config/LeafArchitecture.d.ts +155 -0
  322. package/src/generation/foliage/config/LeafArchitecture.d.ts.map +1 -0
  323. package/src/generation/foliage/config/LeafArchitecture.js +288 -0
  324. package/src/generation/foliage/config/LeafBaseShape.d.ts +14 -0
  325. package/src/generation/foliage/config/LeafBaseShape.d.ts.map +1 -0
  326. package/src/generation/foliage/config/LeafBaseShape.js +21 -0
  327. package/src/generation/foliage/config/LeafLobation.d.ts +10 -0
  328. package/src/generation/foliage/config/LeafLobation.d.ts.map +1 -0
  329. package/src/generation/foliage/config/LeafLobation.js +14 -0
  330. package/src/generation/foliage/config/PhyllotaxisMode.d.ts +11 -0
  331. package/src/generation/foliage/config/PhyllotaxisMode.d.ts.map +1 -1
  332. package/src/generation/foliage/config/PhyllotaxisMode.js +36 -0
  333. package/src/generation/foliage/config/ShootBudZone.d.ts +105 -0
  334. package/src/generation/foliage/config/ShootBudZone.d.ts.map +1 -0
  335. package/src/generation/foliage/config/ShootBudZone.js +219 -0
  336. package/src/generation/foliage/config/ShootGrowthConfig.d.ts +84 -0
  337. package/src/generation/foliage/config/ShootGrowthConfig.d.ts.map +1 -0
  338. package/src/generation/foliage/config/ShootGrowthConfig.js +146 -0
  339. package/src/generation/foliage/config/ShootSystemRule.d.ts +144 -0
  340. package/src/generation/foliage/config/ShootSystemRule.d.ts.map +1 -0
  341. package/src/generation/foliage/config/ShootSystemRule.js +316 -0
  342. package/src/generation/foliage/config/TreeLODConfig.d.ts +1 -1
  343. package/src/generation/foliage/config/TreeLODConfig.js +1 -1
  344. package/src/generation/foliage/config/TreeSpeciesConfig.d.ts +40 -8
  345. package/src/generation/foliage/config/TreeSpeciesConfig.d.ts.map +1 -1
  346. package/src/generation/foliage/config/TreeSpeciesConfig.js +124 -68
  347. package/src/generation/foliage/config/TreeWindConfig.d.ts +23 -10
  348. package/src/generation/foliage/config/TreeWindConfig.d.ts.map +1 -1
  349. package/src/generation/foliage/config/TreeWindConfig.js +34 -25
  350. package/src/generation/foliage/index.d.ts +19 -2
  351. package/src/generation/foliage/index.js +63 -44
  352. package/src/generation/foliage/material/FoliageAtlasCutout.d.ts +59 -0
  353. package/src/generation/foliage/material/FoliageAtlasCutout.d.ts.map +1 -0
  354. package/src/generation/foliage/material/FoliageAtlasCutout.js +93 -0
  355. package/src/generation/foliage/material/compute_foliage_variant_atlas_layout.d.ts +26 -0
  356. package/src/generation/foliage/material/compute_foliage_variant_atlas_layout.d.ts.map +1 -0
  357. package/src/generation/foliage/material/compute_foliage_variant_atlas_layout.js +63 -0
  358. package/src/generation/foliage/material/foliage_atlas_cutout.d.ts +34 -0
  359. package/src/generation/foliage/material/foliage_atlas_cutout.d.ts.map +1 -0
  360. package/src/generation/foliage/material/foliage_atlas_cutout.js +174 -0
  361. package/src/generation/foliage/material/foliage_tile.d.ts +110 -0
  362. package/src/generation/foliage/material/foliage_tile.d.ts.map +1 -0
  363. package/src/generation/foliage/material/foliage_tile.js +286 -0
  364. package/src/generation/foliage/material/leaf_geometry.d.ts +103 -0
  365. package/src/generation/foliage/material/leaf_geometry.d.ts.map +1 -0
  366. package/src/generation/foliage/material/leaf_geometry.js +528 -0
  367. package/src/generation/foliage/material/make_foliage_material.d.ts +13 -33
  368. package/src/generation/foliage/material/make_foliage_material.d.ts.map +1 -1
  369. package/src/generation/foliage/material/make_foliage_material.js +356 -605
  370. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts +39 -4
  371. package/src/generation/foliage/mesh/foliage_collect_attachments.d.ts.map +1 -1
  372. package/src/generation/foliage/mesh/foliage_collect_attachments.js +637 -569
  373. package/src/generation/foliage/mesh/interpolate_ring_skin_weights4.d.ts +48 -0
  374. package/src/generation/foliage/mesh/interpolate_ring_skin_weights4.d.ts.map +1 -0
  375. package/src/generation/foliage/mesh/interpolate_ring_skin_weights4.js +232 -0
  376. package/src/generation/foliage/mesh/tree_mesh_build_bark.d.ts.map +1 -1
  377. package/src/generation/foliage/mesh/tree_mesh_build_bark.js +1023 -1049
  378. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts +9 -2
  379. package/src/generation/foliage/mesh/tree_mesh_build_foliage.d.ts.map +1 -1
  380. package/src/generation/foliage/mesh/tree_mesh_build_foliage.js +467 -454
  381. package/src/generation/foliage/presets/make_tree_species_preset.d.ts.map +1 -1
  382. package/src/generation/foliage/presets/make_tree_species_preset.js +1648 -870
  383. package/src/generation/foliage/skeleton/ShootFrameworkHash.d.ts +35 -0
  384. package/src/generation/foliage/skeleton/ShootFrameworkHash.d.ts.map +1 -0
  385. package/src/generation/foliage/skeleton/ShootFrameworkHash.js +129 -0
  386. package/src/generation/foliage/skeleton/ShootGirth.d.ts +51 -0
  387. package/src/generation/foliage/skeleton/ShootGirth.d.ts.map +1 -0
  388. package/src/generation/foliage/skeleton/ShootGirth.js +212 -0
  389. package/src/generation/foliage/skeleton/ShootLightField.d.ts +73 -0
  390. package/src/generation/foliage/skeleton/ShootLightField.d.ts.map +1 -0
  391. package/src/generation/foliage/skeleton/ShootLightField.js +197 -0
  392. package/src/generation/foliage/skeleton/TreeSkeleton.d.ts +106 -3
  393. package/src/generation/foliage/skeleton/TreeSkeleton.d.ts.map +1 -1
  394. package/src/generation/foliage/skeleton/TreeSkeleton.js +217 -39
  395. package/src/generation/foliage/skeleton/flare_lobe_phase_offset.d.ts +13 -0
  396. package/src/generation/foliage/skeleton/flare_lobe_phase_offset.d.ts.map +1 -0
  397. package/src/generation/foliage/skeleton/flare_lobe_phase_offset.js +16 -0
  398. package/src/generation/foliage/skeleton/foliage_card_density.d.ts +71 -0
  399. package/src/generation/foliage/skeleton/foliage_card_density.d.ts.map +1 -0
  400. package/src/generation/foliage/skeleton/foliage_card_density.js +101 -0
  401. package/src/generation/foliage/skeleton/foliage_senescence.d.ts +101 -0
  402. package/src/generation/foliage/skeleton/foliage_senescence.d.ts.map +1 -0
  403. package/src/generation/foliage/skeleton/foliage_senescence.js +149 -0
  404. package/src/generation/foliage/skeleton/foliage_unit_cards.d.ts +76 -0
  405. package/src/generation/foliage/skeleton/foliage_unit_cards.d.ts.map +1 -0
  406. package/src/generation/foliage/skeleton/foliage_unit_cards.js +95 -0
  407. package/src/generation/foliage/skeleton/shoot_demography.d.ts +22 -0
  408. package/src/generation/foliage/skeleton/shoot_demography.d.ts.map +1 -0
  409. package/src/generation/foliage/skeleton/shoot_demography.js +96 -0
  410. package/src/generation/foliage/skeleton/shoot_geometry.d.ts +66 -0
  411. package/src/generation/foliage/skeleton/shoot_geometry.d.ts.map +1 -0
  412. package/src/generation/foliage/skeleton/shoot_geometry.js +109 -0
  413. package/src/generation/foliage/skeleton/shoot_systems_simulate.d.ts +166 -0
  414. package/src/generation/foliage/skeleton/shoot_systems_simulate.d.ts.map +1 -0
  415. package/src/generation/foliage/skeleton/shoot_systems_simulate.js +588 -0
  416. package/src/generation/foliage/skeleton/tree_skeleton_assign_skin_joints.d.ts +28 -12
  417. package/src/generation/foliage/skeleton/tree_skeleton_assign_skin_joints.d.ts.map +1 -1
  418. package/src/generation/foliage/skeleton/tree_skeleton_assign_skin_joints.js +137 -102
  419. package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.d.ts +112 -0
  420. package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.d.ts.map +1 -0
  421. package/src/generation/foliage/skeleton/tree_skeleton_compute_joint_mechanics.js +408 -0
  422. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.d.ts.map +1 -1
  423. package/src/generation/foliage/skeleton/tree_skeleton_compute_pipe_radii.js +208 -206
  424. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts +5 -2
  425. package/src/generation/foliage/skeleton/tree_skeleton_grow.d.ts.map +1 -1
  426. package/src/generation/foliage/skeleton/tree_skeleton_grow.js +2048 -1871
  427. package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.d.ts +17 -0
  428. package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.d.ts.map +1 -0
  429. package/src/generation/foliage/skeleton/tree_skeleton_grow_shoots.js +231 -0
  430. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_motion.d.ts +14 -0
  431. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_motion.d.ts.map +1 -0
  432. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_motion.js +26 -0
  433. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_parents.d.ts +10 -0
  434. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_parents.d.ts.map +1 -0
  435. package/src/generation/foliage/skeleton/tree_skeleton_read_joint_parents.js +20 -0
  436. package/src/generation/grid/generation/road/GridTaskGenerateRoads.d.ts.map +1 -1
  437. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +4 -1
  438. package/src/generation/markers/GridCellActionPlaceMarkerGroup.d.ts +1 -0
  439. package/src/generation/markers/GridCellActionPlaceMarkerGroup.d.ts.map +1 -1
  440. package/src/generation/markers/GridCellActionPlaceMarkerGroup.js +98 -86
  441. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +263 -0
  442. package/src/shade/descriptor/WebGPUExtensionType.d.ts +17 -0
  443. package/src/shade/descriptor/WebGPUExtensionType.d.ts.map +1 -1
  444. package/src/shade/descriptor/WebGPUExtensionType.js +31 -1
  445. package/src/shade/playground/foliage_wind/FoliageRigOverlay.d.ts +37 -0
  446. package/src/shade/playground/foliage_wind/FoliageRigOverlay.d.ts.map +1 -0
  447. package/src/shade/playground/foliage_wind/FoliageRigOverlay.js +491 -0
  448. package/src/shade/playground/foliage_wind/foliage_world.d.ts.map +1 -1
  449. package/src/shade/playground/foliage_wind/foliage_world.js +600 -602
  450. package/src/shade/playground/foliage_wind/index.html +6 -0
  451. package/src/shade/playground/foliage_wind/main.js +14 -0
  452. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  453. package/src/shade/renderer/animation/GPUAnimationManager.js +2 -1
  454. package/src/shade/renderer/animation/foliage/FoliageWindJoint.d.ts +2 -1
  455. package/src/shade/renderer/animation/foliage/FoliageWindJoint.d.ts.map +1 -1
  456. package/src/shade/renderer/animation/foliage/FoliageWindJoint.js +2 -1
  457. package/src/shade/renderer/animation/foliage/chunk_foliage_wind_step.d.ts.map +1 -1
  458. package/src/shade/renderer/animation/foliage/chunk_foliage_wind_step.js +4 -2
  459. package/src/shade/renderer/buffer/table/GPUTypedTable.js +1 -1
  460. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  461. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +403 -401
  462. package/src/shade/renderer/camera/OrbitalCameraController.d.ts +1 -62
  463. package/src/shade/renderer/camera/OrbitalCameraController.d.ts.map +1 -1
  464. package/src/shade/renderer/camera/OrbitalCameraController.js +1 -374
  465. package/src/shade/renderer/deferred/indirect/chunk_lpv_2d_cache_sample_irradiance.d.ts.map +1 -1
  466. package/src/shade/renderer/deferred/indirect/chunk_lpv_2d_cache_sample_irradiance.js +3 -0
  467. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.d.ts.map +1 -1
  468. package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +6 -1
  469. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.js +1 -1
  470. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +2 -2
  471. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts.map +1 -1
  472. package/src/shade/renderer/fluid/GPUFluidCascade.js +6 -3
  473. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +361 -0
  474. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.d.ts.map +1 -1
  475. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.js +335 -241
  476. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_ghosts.d.ts.map +1 -1
  477. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_ghosts.js +108 -101
  478. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_query_select.d.ts.map +1 -1
  479. package/src/shade/renderer/fluid/forcing/chunk_fluid_forcing_query_select.js +69 -67
  480. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts +7 -6
  481. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.d.ts.map +1 -1
  482. package/src/shade/renderer/fluid/forcing/shader_fluid_forcing_apply.js +341 -287
  483. package/src/shade/renderer/fluid/mac/FluidMacPasses.d.ts +6 -4
  484. package/src/shade/renderer/fluid/mac/FluidMacPasses.d.ts.map +1 -1
  485. package/src/shade/renderer/fluid/mac/FluidMacPasses.js +427 -400
  486. package/src/shade/renderer/fluid/mac/chunk_fluid_mac_seams.d.ts +10 -5
  487. package/src/shade/renderer/fluid/mac/chunk_fluid_mac_seams.d.ts.map +1 -1
  488. package/src/shade/renderer/fluid/mac/chunk_fluid_mac_seams.js +532 -190
  489. package/src/shade/renderer/fluid/mac/chunk_v3_mac_lattice_corners.d.ts.map +1 -1
  490. package/src/shade/renderer/fluid/mac/chunk_v3_mac_lattice_corners.js +231 -237
  491. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts +2 -2
  492. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts.map +1 -1
  493. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.js +282 -245
  494. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_predict.d.ts +1 -3
  495. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_predict.d.ts.map +1 -1
  496. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_predict.js +139 -109
  497. package/src/shade/renderer/fluid/mac/shader_fluid_mac_curl.d.ts +6 -6
  498. package/src/shade/renderer/fluid/mac/shader_fluid_mac_curl.d.ts.map +1 -1
  499. package/src/shade/renderer/fluid/mac/shader_fluid_mac_curl.js +257 -222
  500. package/src/shade/renderer/fluid/mac/shader_fluid_mac_divergence.d.ts +8 -7
  501. package/src/shade/renderer/fluid/mac/shader_fluid_mac_divergence.d.ts.map +1 -1
  502. package/src/shade/renderer/fluid/mac/shader_fluid_mac_divergence.js +119 -92
  503. package/src/shade/renderer/fluid/mac/shader_fluid_mac_gradient.d.ts +6 -9
  504. package/src/shade/renderer/fluid/mac/shader_fluid_mac_gradient.d.ts.map +1 -1
  505. package/src/shade/renderer/fluid/mac/shader_fluid_mac_gradient.js +229 -181
  506. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +17 -7
  507. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts.map +1 -1
  508. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.js +634 -621
  509. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.d.ts +8 -6
  510. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.d.ts.map +1 -1
  511. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_residual_restrict.js +166 -149
  512. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.d.ts +8 -6
  513. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.d.ts.map +1 -1
  514. package/src/shade/renderer/fluid/pressure/shader_fluid_mg_smooth.js +219 -183
  515. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.d.ts.map +1 -1
  516. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.js +142 -122
  517. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +674 -663
  518. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +1 -1
  519. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  520. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +63 -29
  521. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
  522. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +77 -87
  523. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.d.ts.map +1 -1
  524. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/meshopt_clusterizer.js +4 -3
  525. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts +11 -11
  526. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts.map +1 -1
  527. package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.js +17 -19
  528. package/src/shade/renderer/global_illumination/lpv/graph_bake_irradiance_lookup_2d.js +4 -4
  529. package/src/shade/renderer/global_illumination/probe/octahedral/sample/chunk_lpv_mesh_interpolate_radiance_via_atlas.d.ts.map +1 -1
  530. package/src/shade/renderer/global_illumination/probe/octahedral/sample/chunk_lpv_mesh_interpolate_radiance_via_atlas.js +5 -1
  531. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  532. package/src/shade/renderer/loader/gltf/load_gltf.js +11 -35
  533. package/src/shade/renderer/loader/usd/load_usd.d.ts.map +1 -1
  534. package/src/shade/renderer/loader/usd/load_usd.js +6 -19
  535. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.d.ts.map +1 -1
  536. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +199 -193
  537. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -1
  538. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +151 -143
  539. package/src/shade/renderer/postprocess/bloom/chunk_karis_blur4x4_sample.d.ts.map +1 -1
  540. package/src/shade/renderer/postprocess/bloom/chunk_karis_blur4x4_sample.js +11 -5
  541. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.d.ts.map +1 -1
  542. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.js +6 -18
  543. package/src/shade/renderer/postprocess/gtao/GTAO.js +1 -1
  544. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.d.ts.map +1 -1
  545. package/src/shade/renderer/postprocess/gtao/ao_reproject_shader.js +3 -5
  546. package/src/shade/renderer/postprocess/gtao/fragment_shader_gtao_with_irradiance.js +1 -1
  547. package/src/shade/renderer/postprocess/nss/chunk_nss_luma_derivative.d.ts.map +1 -1
  548. package/src/shade/renderer/postprocess/nss/chunk_nss_luma_derivative.js +7 -3
  549. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  550. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +4 -2
  551. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.d.ts.map +1 -1
  552. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +4 -1
  553. package/src/shade/renderer/postprocess/ssr/downsample_karis.d.ts.map +1 -1
  554. package/src/shade/renderer/postprocess/ssr/downsample_karis.js +6 -3
  555. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.d.ts.map +1 -1
  556. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_temporal_accumulate.js +4 -3
  557. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.d.ts.map +1 -1
  558. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader.js +4 -1
  559. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
  560. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +4 -2
  561. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap.d.ts.map +1 -1
  562. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap.js +35 -39
  563. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap_inv.d.ts.map +1 -1
  564. package/src/shade/renderer/postprocess/taa/chunk_taa_tonemap_inv.js +20 -17
  565. package/src/shade/renderer/postprocess/taa/shader_taa.d.ts.map +1 -1
  566. package/src/shade/renderer/postprocess/taa/shader_taa.js +6 -3
  567. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  568. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +3 -1
  569. package/src/shade/renderer/rasterize/native/avboit/avboit_draw_fragment_body.js +1 -1
  570. package/src/shade/renderer/rasterize/native/avboit/chunk_avboit_splat_slice.js +2 -2
  571. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +1 -1
  572. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.d.ts.map +1 -1
  573. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_integrate.js +92 -83
  574. package/src/shade/renderer/rasterize/native/avboit/shader_avboit_splat.js +1 -1
  575. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  576. package/src/shade/renderer/shader/ShaderDescriptor.js +12 -4
  577. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.d.ts.map +1 -1
  578. package/src/shade/renderer/shader/chunk/bvh/chunk_bvh_query_aabb_cooperative.js +294 -286
  579. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_interpolate_probe_irradiance8.js +5 -5
  580. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe.d.ts.map +1 -1
  581. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe.js +5 -3
  582. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe_irradiance.d.ts.map +1 -1
  583. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mesh_interpolate_probe_irradiance.js +9 -6
  584. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_sample_sh3_at.d.ts.map +1 -1
  585. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_sample_sh3_at.js +33 -38
  586. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  587. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +690 -675
  588. package/src/shade/renderer/shader/type/WebGPUArray.d.ts +3 -0
  589. package/src/shade/renderer/shader/type/WebGPUArray.d.ts.map +1 -1
  590. package/src/shade/renderer/shader/type/WebGPUArray.js +185 -167
  591. package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
  592. package/src/shade/renderer/shader/type/WebGPUStruct.js +461 -455
  593. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.d.ts.map +1 -1
  594. package/src/shade/renderer/shader/type/serde/read_gpu_typed_buffer.js +335 -211
  595. package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.d.ts.map +1 -1
  596. package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.js +329 -221
  597. package/src/shade/renderer/shader/type/type_has_f16.d.ts +12 -0
  598. package/src/shade/renderer/shader/type/type_has_f16.d.ts.map +1 -0
  599. package/src/shade/renderer/shader/type/type_has_f16.js +66 -0
  600. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
  601. package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +16 -2
  602. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts.map +1 -1
  603. package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.js +5 -2
  604. package/src/shade/renderer/water/shader_water_avboit.js +1 -1
  605. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  606. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +260 -23
  607. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts +6 -7
  608. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  609. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +269 -69
  610. package/src/shade/wgsl/validate_wgsl_source.d.ts.map +1 -1
  611. package/src/shade/wgsl/validate_wgsl_source.js +27 -1
  612. package/src/view/controller/controls/ListController.d.ts.map +1 -1
  613. package/src/view/controller/controls/ListController.js +118 -113
  614. package/src/view/controller/controls/Vector1Control.js +97 -97
  615. package/src/view/elements/image/SvgImageView.d.ts +1 -0
  616. package/src/view/elements/image/SvgImageView.d.ts.map +1 -1
  617. package/src/view/elements/image/SvgImageView.js +92 -90
  618. package/src/view/minimap/dom/MinimapTerrainView.d.ts +1 -0
  619. package/src/view/minimap/dom/MinimapTerrainView.d.ts.map +1 -1
  620. package/src/view/minimap/dom/MinimapTerrainView.js +151 -147
  621. package/src/view/tooltip/gml/TooltipParser.js +2 -2
  622. package/src/engine/network/PriorityFetch.d.ts +0 -59
  623. package/src/engine/network/PriorityFetch.d.ts.map +0 -1
  624. package/src/engine/network/PriorityFetch.js +0 -193
  625. package/src/generation/foliage/config/BranchLevelConfig.d.ts.map +0 -1
@@ -1,621 +1,634 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { sor_optimal_omega } from "../../../../core/math/linalg/sor_optimal_omega.js";
3
- import { GRID_TILE_SIZE } from "../../shader/chunk/grid/chunk_grid_tile_window.js";
4
- import { write_gpu_typed_buffer } from "../../shader/type/serde/write_gpu_typed_buffer.js";
5
- import {
6
- FLUID_MG_SMOOTH_SWEEP_LIMIT,
7
- FLUID_MG_SMOOTH_SWEEPS,
8
- fluid_mg_edge_weight,
9
- fluid_mg_level_sizes,
10
- FluidMGColour
11
- } from "./FLUID_MG.js";
12
- import { shader_fluid_mg_coarse_weights } from "./shader_fluid_mg_coarse_weights.js";
13
- import { shader_fluid_mg_coarsest } from "./shader_fluid_mg_coarsest.js";
14
- import { shader_fluid_mg_level0_weights } from "./shader_fluid_mg_level0_weights.js";
15
- import { shader_fluid_mg_residual_restrict } from "./shader_fluid_mg_residual_restrict.js";
16
- import { shader_fluid_mg_smooth } from "./shader_fluid_mg_smooth.js";
17
- import { FLUID_MG_PASS_STRUCT } from "./FLUID_MG_PASS_STRUCT.js";
18
-
19
- /**
20
- * @typedef {import("../../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext} ShadeGPUCommandContext
21
- */
22
-
23
- /**
24
- * A solve's inputs, as {@link GPUFluidPressureSolve#record_v_cycles} documents them, with `cycles` for
25
- * the V-cycles or `sweeps` for SOR.
26
- *
27
- * @typedef {{pressure: GPUBuffer, window: {corner: ArrayLike<number>, corner_storage: ArrayLike<number>, size: ArrayLike<number>, period: ArrayLike<number>}, rhs: GPUBuffer, solid: GPUBuffer, h: number, ghost_pressure: GPUBuffer, cycles?: number, sweeps?: number}} FluidPressureSolveInputs
28
- */
29
-
30
- const ARM_V_CYCLES = 0;
31
- const ARM_SOR = 1;
32
-
33
- const BUFFER_USAGE = () => GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST;
34
-
35
- /**
36
- * Bytes between two passes' params in the params buffer: WebGPU's floor for
37
- * `minUniformBufferOffsetAlignment`, which every device meets.
38
- * @type {number}
39
- */
40
- const PARAMS_STRIDE = 256;
41
-
42
- /**
43
- * Pass parameters as {@link FLUID_MG_PASS_STRUCT} lays them out, held so a frame writes into them
44
- * rather than making new ones.
45
- */
46
- function make_pass_values() {
47
- return {
48
- corner: new Int32Array(3),
49
- level: 0,
50
- size: new Uint32Array(3),
51
- colours: 0,
52
- input_corner_storage: new Uint32Array(3),
53
- sweeps: 0,
54
- input_period: new Uint32Array(3),
55
- prolong: 0,
56
- output_corner_storage: new Uint32Array(3),
57
- omega: 1,
58
- output_period: new Uint32Array(3),
59
- h2: 0,
60
- coarse_size: new Uint32Array(3),
61
- edge_weight: 1,
62
- };
63
- }
64
-
65
- /**
66
- * A window-local, linear buffer as a window: storage corner 0, a period of its size.
67
- */
68
- function set_linear(corner_storage, period, size) {
69
- corner_storage.fill(0);
70
- period.set(size);
71
- }
72
-
73
- /**
74
- * The GPU fluid's pressure solve for one cascade window (GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md §8):
75
- * `Σ_f w_f·(p_nb − p) / H² = f` for the physical kinematic pressure `p` (m²/s²), with `f = div/dt`, a
76
- * solid face closed and the window's edge a Dirichlet boundary at the ghost cell's centre. The
77
- * cascade's parent's pressure at the ghost cell's centre is the ghost (`chunk_fluid_pressure_ghost`), as
78
- * the step's pressure ghosts hold it.
79
- *
80
- * It owns its level transients, sized by the window and made when the window's size or the device
81
- * changes, never per frame. Each `record_*` adds one graph pass that dispatches everything; each
82
- * `encode_*` dispatches the same into the encoder of a pass the caller records itself:
83
- *
84
- * - **Weights**, once a solve: the finest level's words from the solid flags, then each coarse level's
85
- * from the one above (`shader_fluid_mg_level0_weights`, `shader_fluid_mg_coarse_weights`). The SOR
86
- * arm needs the finest only.
87
- * - **{@link record_v_cycles}**, V-cycles of `3L − 2` dispatches over `L` levels: on each level above
88
- * the coarsest a smoother of {@link FLUID_MG_SMOOTH_SWEEPS} sweeps and a residual + restriction; the
89
- * coarsest level in one workgroup; then each level back up, one smoother whose load prolongs the
90
- * level below in. At 64×32×64, four levels: 10 dispatches a V-cycle, plus 4 for the weights.
91
- * - **{@link record_sor}**, red-black SOR on the finest level at `sor_optimal_omega`, one colour a
92
- * dispatch: `2K` dispatches for `K` sweeps, plus 1 for the weights.
93
- *
94
- * A smoother never writes the buffer it reads, so the finest level goes pressure → scratch → pressure.
95
- *
96
- * Non-finite pressure, error and RHS are read as 0, and {@link nonfinite_counter} adds one for each such
97
- * value a pass's owning invocation read. It is never reset.
98
- *
99
- * The window's edge is a Dirichlet boundary at the ghost cell's centre: the cascade's parent's
100
- * pressure there, read from `ghost_pressure`, the step's pressure ghosts (`shader_fluid_cascade_ghosts`);
101
- * the far field, a parent with no window, reads 0.
102
- */
103
- export class GPUFluidPressureSolve {
104
- /**
105
- * Offer the compute pipelines the multigrid and SOR pressure solvers dispatch — see
106
- * {@link PipelineEnquiry}.
107
- *
108
- * @param {import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry} enquiry
109
- */
110
- static enquirePipelines(enquiry) {
111
- enquiry.compute.push(
112
- shader_fluid_mg_smooth.pipeline,
113
- shader_fluid_mg_residual_restrict.pipeline,
114
- shader_fluid_mg_coarsest.pipeline,
115
- shader_fluid_mg_level0_weights.pipeline,
116
- shader_fluid_mg_coarse_weights.pipeline,
117
- );
118
- }
119
-
120
- /** @type {GPUDevice|null} */
121
- #device = null;
122
- /** @type {number[][]} the cells of every level, finest first */
123
- #sizes = [];
124
- /**
125
- * Per level: its weight words; below the finest its RHS (`f`), its error after the up leg (`a`,
126
- * zeroed by the restriction) and after the down leg (`b`).
127
- * @type {{words: GPUBuffer, f: GPUBuffer|null, a: GPUBuffer|null, b: GPUBuffer|null, groups: number[]}[]}
128
- */
129
- #levels = [];
130
- /** @type {GPUBuffer|null} the finest level after the down leg */
131
- #scratch = null;
132
- /** @type {GPUBuffer|null} */
133
- #nonfinite = null;
134
- /** @type {GPUBuffer|null} every pass's params, one {@link PARAMS_STRIDE} slot each */
135
- #params = null;
136
- /** @type {ArrayBuffer|null} the bytes {@link #params} is written from */
137
- #params_data = null;
138
- /** @type {{values: object, binding: GPUBufferBinding}[]} the slots, in the buffer's order */
139
- #slots = [];
140
-
141
- /** Held params and bindings, one per dispatch the solve can make. */
142
- #weights = [];
143
- #down = [];
144
- #restrict = [];
145
- #up = [];
146
- #coarsest = null;
147
- #sor = [];
148
-
149
- /**
150
- * One `u32`: how many non-finite values the solves recorded so far read and zeroed. Made with the
151
- * level transients.
152
- * @returns {GPUBuffer|null}
153
- */
154
- get nonfinite_counter() {
155
- return this.#nonfinite;
156
- }
157
-
158
- /**
159
- * Record V-cycles into a pass of `graph`. Nothing is recorded for 0 cycles.
160
- *
161
- * @param {object} options
162
- * @param {import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} options.graph
163
- * @param {GPUBuffer} options.pressure the cascade's pressure, read as the warm start and written with
164
- * the answer: `f32` at each storage cell of `window`, x fastest
165
- * @param {{corner: ArrayLike<number>, corner_storage: ArrayLike<number>, size: ArrayLike<number>, period: ArrayLike<number>}} options.window
166
- * where the pressure is stored, as {@link GRID_TILE_WINDOW_STRUCT}; its size is the solve's cells,
167
- * powers of two
168
- * @param {GPUBuffer} options.rhs `f = div/dt` per cell, window-local and linear
169
- * @param {GPUBuffer} options.solid solid flags per cell (bit 0), window-local and linear
170
- * @param {number} options.h the cell's size, metres
171
- * @param {GPUBuffer} options.ghost_pressure the finest level's Dirichlet ghosts (`chunk_fluid_pressure_ghost`):
172
- * the parent's pressure at every cell one past the window, `array<f32>` over the window grown by
173
- * one cell on every side, x fastest, as `shader_fluid_cascade_ghosts` writes it
174
- * @param {number} options.cycles V-cycles
175
- */
176
- record_v_cycles({ graph, pressure, window, rhs, solid, h, ghost_pressure, cycles }) {
177
- assert.isNonNegativeInteger(cycles, 'cycles');
178
-
179
- this.#record(graph, ARM_V_CYCLES, cycles, { pressure, window, rhs, solid, h, ghost_pressure });
180
- }
181
-
182
- /**
183
- * {@link record_v_cycles} into `encoder` itself, for a caller whose own frame-graph pass records
184
- * the solve among its other dispatches, as a cascade's step does. Nothing is encoded for 0 cycles.
185
- * `inputs` is read, not kept.
186
- *
187
- * @param {ShadeGPUCommandContext} encoder the encoder a frame-graph pass's callback was handed
188
- * @param {FluidPressureSolveInputs} inputs
189
- */
190
- encode_v_cycles(encoder, inputs) {
191
- assert.defined(encoder, 'encoder');
192
- assert.isNonNegativeInteger(inputs.cycles, 'cycles');
193
-
194
- if (this.#validate(inputs.cycles, inputs)) {
195
- this.#encode(encoder, ARM_V_CYCLES, inputs.cycles, inputs);
196
- }
197
- }
198
-
199
- /**
200
- * {@link record_sor} into `encoder` itself, as {@link encode_v_cycles}. Nothing is encoded for 0
201
- * sweeps.
202
- *
203
- * @param {ShadeGPUCommandContext} encoder
204
- * @param {FluidPressureSolveInputs} inputs
205
- */
206
- encode_sor(encoder, inputs) {
207
- assert.defined(encoder, 'encoder');
208
- assert.isNonNegativeInteger(inputs.sweeps, 'sweeps');
209
-
210
- if (this.#validate(inputs.sweeps, inputs)) {
211
- this.#encode(encoder, ARM_SOR, inputs.sweeps, inputs);
212
- }
213
- }
214
-
215
- /**
216
- * Record red-black SOR sweeps on the finest level into a pass of `graph`, one colour a dispatch.
217
- * Nothing is recorded for 0 sweeps.
218
- *
219
- * @param {object} options
220
- * @param {import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} options.graph
221
- * @param {GPUBuffer} options.pressure as {@link record_v_cycles}
222
- * @param {{corner: ArrayLike<number>, corner_storage: ArrayLike<number>, size: ArrayLike<number>, period: ArrayLike<number>}} options.window
223
- * @param {GPUBuffer} options.rhs
224
- * @param {GPUBuffer} options.solid
225
- * @param {number} options.h
226
- * @param {GPUBuffer} options.ghost_pressure
227
- * @param {number} options.sweeps red-black sweeps
228
- */
229
- record_sor({ graph, pressure, window, rhs, solid, h, ghost_pressure, sweeps }) {
230
- assert.isNonNegativeInteger(sweeps, 'sweeps');
231
-
232
- this.#record(graph, ARM_SOR, sweeps, { pressure, window, rhs, solid, h, ghost_pressure });
233
- }
234
-
235
- #record(graph, arm, count, inputs) {
236
- assert.defined(graph, 'graph');
237
-
238
- if (!this.#validate(count, inputs)) {
239
- return;
240
- }
241
-
242
- const pass = graph.add('Fluid / pressure solve', { solve: this, arm, count, inputs }, GPUFluidPressureSolve.#execute);
243
-
244
- pass.make_side_effect();
245
- }
246
-
247
- /**
248
- * Check a solve's inputs.
249
- *
250
- * @param {number} count
251
- * @param {FluidPressureSolveInputs} inputs
252
- * @returns {boolean} whether there is anything to encode
253
- */
254
- #validate(count, { pressure, window, rhs, solid, h, ghost_pressure }) {
255
- assert.defined(pressure, 'pressure');
256
- assert.defined(rhs, 'rhs');
257
- assert.defined(solid, 'solid');
258
- assert.defined(ghost_pressure, 'ghost_pressure');
259
- assert.notEqual(ghost_pressure, pressure, 'the ghosts are not the cascade\'s pressure');
260
- assert.isFinite(h, 'h');
261
- assert.greaterThan(h, 0, 'h');
262
-
263
- const { size, period } = window;
264
- const cells = size[0] * size[1] * size[2];
265
-
266
- for (let axis = 0; axis < 3; axis++) {
267
- assert.isPositiveInteger(size[axis], 'window.size');
268
- assert.ok((size[axis] & (size[axis] - 1)) === 0, 'window.size is a power of two');
269
- assert.lessThanOrEqual(size[axis], period[axis], 'window.size within its period');
270
- }
271
-
272
- assert.greaterThanOrEqual(pressure.size, 4 * period[0] * period[1] * period[2], 'pressure covers the period');
273
- assert.greaterThanOrEqual(rhs.size, 4 * cells, 'rhs covers the window');
274
- assert.greaterThanOrEqual(solid.size, 4 * cells, 'solid covers the window');
275
-
276
- return count > 0;
277
- }
278
-
279
- /**
280
- * @param {{solve: GPUFluidPressureSolve, arm: number, count: number, inputs: FluidPressureSolveInputs}} data
281
- * @param {*} _resources
282
- * @param {{encoder: ShadeGPUCommandContext}} context
283
- */
284
- static #execute(data, _resources, context) {
285
- data.solve.#encode(context.encoder, data.arm, data.count, data.inputs);
286
- }
287
-
288
- /**
289
- * @param {ShadeGPUCommandContext} encoder
290
- * @param {number} arm
291
- * @param {number} count
292
- * @param {FluidPressureSolveInputs} inputs
293
- */
294
- #encode(encoder, arm, count, inputs) {
295
- this.#configure(encoder.device, inputs.window.size);
296
- this.#prepare(encoder, inputs);
297
-
298
- const last = this.#levels.length - 1;
299
-
300
- dispatch_levelled(encoder, shader_fluid_mg_level0_weights, this.#weights[0], this.#levels[0].groups);
301
-
302
- if (arm === ARM_SOR) {
303
- for (let k = 0; k < count; k++) {
304
- dispatch_smooth(encoder, this.#sor[0], this.#levels[0].groups);
305
- dispatch_smooth(encoder, this.#sor[1], this.#levels[0].groups);
306
- }
307
-
308
- return;
309
- }
310
-
311
- for (let l = 1; l <= last; l++) {
312
- dispatch_levelled(encoder, shader_fluid_mg_coarse_weights, this.#weights[l], this.#levels[l].groups);
313
- }
314
-
315
- for (let cycle = 0; cycle < count; cycle++) {
316
- for (let l = 0; l < last; l++) {
317
- dispatch_smooth(encoder, this.#down[l], this.#levels[l].groups);
318
- dispatch_levelled(encoder, shader_fluid_mg_residual_restrict, this.#restrict[l], this.#levels[l].groups);
319
- }
320
-
321
- shader_fluid_mg_coarsest.dispatch({ encoder, bindings: this.#coarsest });
322
-
323
- for (let l = last - 1; l >= 0; l--) {
324
- dispatch_smooth(encoder, this.#up[l], this.#levels[l].groups);
325
- }
326
- }
327
- }
328
-
329
- /**
330
- * The level transients for a window of `size` cells on `device`, made anew only when either
331
- * changed.
332
- */
333
- #configure(device, size) {
334
- const sizes = this.#sizes;
335
-
336
- if (device === this.#device && sizes.length > 0
337
- && sizes[0][0] === size[0] && sizes[0][1] === size[1] && sizes[0][2] === size[2]) {
338
- return;
339
- }
340
-
341
- this.dispose();
342
-
343
- this.#device = device;
344
- this.#sizes = fluid_mg_level_sizes(size[0], size[1], size[2]);
345
-
346
- const buffer = (label, cells) => device.createBuffer({ label: `Fluid / pressure / ${label}`, size: 4 * cells, usage: BUFFER_USAGE() });
347
-
348
- this.#levels = this.#sizes.map((s, l) => {
349
- const cells = s[0] * s[1] * s[2];
350
- const coarse = l > 0;
351
-
352
- return {
353
- words: buffer(`level ${l} weights`, cells),
354
- f: coarse ? buffer(`level ${l} rhs`, cells) : null,
355
- a: coarse ? buffer(`level ${l} error`, cells) : null,
356
- b: coarse ? buffer(`level ${l} error (down)`, cells) : null,
357
- groups: s.map((n, axis) => Math.ceil(n / GRID_TILE_SIZE[axis])),
358
- };
359
- });
360
-
361
- const s0 = this.#sizes[0];
362
- this.#scratch = buffer('scratch', s0[0] * s0[1] * s0[2]);
363
- this.#nonfinite = buffer('non-finite count', 1);
364
-
365
- const last = this.#levels.length - 1;
366
-
367
- // weights per level; down, restrict and up per level above the coarsest; the coarsest; SOR red and black
368
- const slot_count = this.#levels.length + 3 * last + 1 + 2;
369
-
370
- this.#params = device.createBuffer({ label: 'Fluid / pressure / pass params', size: slot_count * PARAMS_STRIDE, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
371
- this.#params_data = new ArrayBuffer(slot_count * PARAMS_STRIDE);
372
- this.#slots = [];
373
-
374
- const params = () => {
375
- const slot = { values: make_pass_values(), binding: { buffer: this.#params, offset: this.#slots.length * PARAMS_STRIDE, size: FLUID_MG_PASS_STRUCT.size } };
376
-
377
- this.#slots.push(slot);
378
-
379
- return slot;
380
- };
381
-
382
- const smoother = () => ({ slot: params(), params: null, input: null, output: null, rhs: null, words: null, coarse: null, nonfinite: this.#nonfinite, ghost_pressure: null });
383
-
384
- this.#weights = this.#levels.map(() => ({ slot: params(), params: null, solid: null, fine_words: null, words: null }));
385
- this.#down = [];
386
- this.#restrict = [];
387
- this.#up = [];
388
-
389
- for (let l = 0; l < last; l++) {
390
- this.#down.push(smoother());
391
- this.#restrict.push({ slot: params(), params: null, input: null, rhs: null, words: null, coarse_rhs: null, coarse_error: null, nonfinite: this.#nonfinite, ghost_pressure: null });
392
- this.#up.push(smoother());
393
- }
394
-
395
- this.#coarsest = { slot: params(), params: null, values: null, rhs: null, words: null, nonfinite: this.#nonfinite, ghost_pressure: null };
396
- this.#sor = [smoother(), smoother()];
397
-
398
- for (const bindings of [...this.#weights, ...this.#down, ...this.#restrict, ...this.#up, this.#coarsest, ...this.#sor]) {
399
- bindings.params = bindings.slot.binding;
400
- }
401
-
402
- assert.equal(this.#slots.length, slot_count, 'slots');
403
- }
404
-
405
- /**
406
- * Every held pass's params and bindings for this solve's inputs.
407
- *
408
- * @param {ShadeGPUCommandContext} encoder
409
- * @param {FluidPressureSolveInputs} inputs
410
- */
411
- #prepare(encoder, { pressure, window, rhs, solid, h, ghost_pressure }) {
412
- const sizes = this.#sizes;
413
- const levels = this.#levels;
414
- const last = levels.length - 1;
415
-
416
- for (let l = 0; l <= last; l++) {
417
- const weights = this.#weights[l];
418
-
419
- set_level_params(weights.slot.values, sizes, l, h, window);
420
- weights.solid = solid;
421
- weights.fine_words = l === 0 ? null : levels[l - 1].words;
422
- weights.words = levels[l].words;
423
- }
424
-
425
- for (let l = 0; l < last; l++) {
426
- const level_rhs = l === 0 ? rhs : levels[l].f;
427
-
428
- const down = this.#down[l];
429
-
430
- set_level_params(down.slot.values, sizes, l, h, window);
431
- down.input = l === 0 ? pressure : levels[l].a;
432
- down.output = l === 0 ? this.#scratch : levels[l].b;
433
- down.rhs = level_rhs;
434
- down.words = levels[l].words;
435
- // a smoother that prolongs nothing binds the RHS in its place, read-only twice
436
- down.coarse = level_rhs;
437
-
438
- if (l === 0) {
439
- set_pressure_input(down.slot.values, window);
440
- }
441
-
442
- const restrict = this.#restrict[l];
443
-
444
- set_level_params(restrict.slot.values, sizes, l, h, window);
445
- restrict.input = down.output;
446
- restrict.rhs = level_rhs;
447
- restrict.words = levels[l].words;
448
- restrict.coarse_rhs = levels[l + 1].f;
449
- restrict.coarse_error = levels[l + 1].a;
450
-
451
- const up = this.#up[l];
452
-
453
- set_level_params(up.slot.values, sizes, l, h, window);
454
- up.slot.values.prolong = 1;
455
- up.input = down.output;
456
- up.output = l === 0 ? pressure : levels[l].a;
457
- up.rhs = level_rhs;
458
- up.words = levels[l].words;
459
- up.coarse = levels[l + 1].a;
460
-
461
- if (l === 0) {
462
- set_pressure_output(up.slot.values, window);
463
- }
464
- }
465
-
466
- const coarsest = this.#coarsest;
467
-
468
- set_level_params(coarsest.slot.values, sizes, last, h, window);
469
- coarsest.values = last === 0 ? pressure : levels[last].a;
470
- coarsest.rhs = last === 0 ? rhs : levels[last].f;
471
- coarsest.words = levels[last].words;
472
-
473
- if (last === 0) {
474
- set_pressure_input(coarsest.slot.values, window);
475
- }
476
-
477
- const omega = sor_optimal_omega(sizes[0][0], sizes[0][1], sizes[0][2]);
478
-
479
- for (let colour = 0; colour < 2; colour++) {
480
- const sor = this.#sor[colour];
481
-
482
- set_level_params(sor.slot.values, sizes, 0, h, window);
483
- sor.slot.values.colours = colour === 0 ? FluidMGColour.Red : FluidMGColour.Black;
484
- sor.slot.values.sweeps = 1;
485
- sor.slot.values.omega = omega;
486
- sor.input = colour === 0 ? pressure : this.#scratch;
487
- sor.output = colour === 0 ? this.#scratch : pressure;
488
- sor.rhs = rhs;
489
- sor.words = levels[0].words;
490
- sor.coarse = rhs;
491
-
492
- if (colour === 0) {
493
- set_pressure_input(sor.slot.values, window);
494
- } else {
495
- set_pressure_output(sor.slot.values, window);
496
- }
497
- }
498
-
499
- // the step's ghosts, for the finest level's Dirichlet boundary; the passes below it bind them unread
500
- for (const bindings of [...this.#down, ...this.#restrict, ...this.#up, this.#coarsest, ...this.#sor]) {
501
- bindings.ghost_pressure = ghost_pressure;
502
- }
503
-
504
- // every slot in one write, recorded in order with the passes, so a second solve recorded into
505
- // the same frame cannot overwrite what this one's passes read
506
- const slots = this.#slots;
507
-
508
- for (let i = 0; i < slots.length; i++) {
509
- write_gpu_typed_buffer(slots[i].values, FLUID_MG_PASS_STRUCT, this.#params_data, i * PARAMS_STRIDE);
510
- }
511
-
512
- encoder.writeBuffer(this.#params, 0, this.#params_data, 0, this.#params_data.byteLength);
513
- }
514
-
515
- /**
516
- * Release the level transients and the counter.
517
- */
518
- dispose() {
519
- for (const level of this.#levels) {
520
- level.words.destroy();
521
- level.f?.destroy();
522
- level.a?.destroy();
523
- level.b?.destroy();
524
- }
525
-
526
- this.#scratch?.destroy();
527
- this.#nonfinite?.destroy();
528
- this.#params?.destroy();
529
-
530
- this.#levels = [];
531
- this.#sizes = [];
532
- this.#scratch = null;
533
- this.#nonfinite = null;
534
- this.#params = null;
535
- this.#params_data = null;
536
- this.#slots = [];
537
- this.#device = null;
538
- this.#weights = [];
539
- this.#down = [];
540
- this.#restrict = [];
541
- this.#up = [];
542
- this.#coarsest = null;
543
- this.#sor = [];
544
- }
545
- }
546
-
547
- /**
548
- * A level's pass params as a smoother of both colours at ω = 1 with no prolongation, reading and
549
- * writing window-local linear buffers.
550
- *
551
- * @param {object} params
552
- * @param {number[][]} sizes every level's cells
553
- * @param {number} l the level
554
- * @param {number} h the finest cell's size
555
- * @param {{corner: ArrayLike<number>}} window
556
- */
557
- function set_level_params(params, sizes, l, h, window) {
558
- const size = sizes[l];
559
- const H = h * 2 ** l;
560
-
561
- params.size.set(size);
562
- params.level = l;
563
- params.h2 = H * H;
564
- params.edge_weight = fluid_mg_edge_weight(l);
565
- set_linear(params.input_corner_storage, params.input_period, size);
566
- set_linear(params.output_corner_storage, params.output_period, size);
567
-
568
- if (l === 0) {
569
- params.corner.set(window.corner);
570
- } else {
571
- params.corner.fill(0);
572
- }
573
-
574
- if (l + 1 < sizes.length) {
575
- params.coarse_size.set(sizes[l + 1]);
576
- } else {
577
- params.coarse_size.fill(0);
578
- }
579
-
580
- params.colours = FluidMGColour.Both;
581
- params.sweeps = FLUID_MG_SMOOTH_SWEEPS;
582
- params.omega = 1;
583
- params.prolong = 0;
584
- }
585
-
586
- /**
587
- * The pass reads the cascade's pressure, through its window.
588
- */
589
- function set_pressure_input(params, window) {
590
- params.input_corner_storage.set(window.corner_storage);
591
- params.input_period.set(window.period);
592
- }
593
-
594
- /**
595
- * The pass writes the cascade's pressure, through its window.
596
- */
597
- function set_pressure_output(params, window) {
598
- params.output_corner_storage.set(window.corner_storage);
599
- params.output_period.set(window.period);
600
- }
601
-
602
- /**
603
- * A pass over a level, one workgroup per 8×8×4 cells.
604
- */
605
- function dispatch_levelled(encoder, shader, bindings, groups) {
606
- shader.dispatch({ encoder, bindings, group_count_x: groups[0], group_count_y: groups[1], group_count_z: groups[2] });
607
- }
608
-
609
- /**
610
- * A smoother pass. Both colours against one frozen halo diverge for ω > 1, and one colour twice in a
611
- * dispatch is not a sweep.
612
- */
613
- function dispatch_smooth(encoder, bindings, groups) {
614
- const params = bindings.slot.values;
615
-
616
- assert.ok(params.colours !== FluidMGColour.Both || params.omega === 1, `both colours in one smoother dispatch need omega = 1, not ${params.omega}`);
617
- assert.ok(params.colours === FluidMGColour.Both || params.sweeps === 1, 'one colour is one sweep a dispatch');
618
- assert.lessThanOrEqual(params.sweeps, FLUID_MG_SMOOTH_SWEEP_LIMIT, 'sweeps');
619
-
620
- dispatch_levelled(encoder, shader_fluid_mg_smooth, bindings, groups);
621
- }
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { sor_optimal_omega } from "../../../../core/math/linalg/sor_optimal_omega.js";
3
+ import { GRID_TILE_SIZE } from "../../shader/chunk/grid/chunk_grid_tile_window.js";
4
+ import { write_gpu_typed_buffer } from "../../shader/type/serde/write_gpu_typed_buffer.js";
5
+ import {
6
+ FLUID_MG_SMOOTH_SWEEP_LIMIT,
7
+ FLUID_MG_SMOOTH_SWEEPS,
8
+ fluid_mg_edge_weight,
9
+ fluid_mg_level_sizes,
10
+ FluidMGColour
11
+ } from "./FLUID_MG.js";
12
+ import { shader_fluid_mg_coarse_weights } from "./shader_fluid_mg_coarse_weights.js";
13
+ import { shader_fluid_mg_coarsest } from "./shader_fluid_mg_coarsest.js";
14
+ import { shader_fluid_mg_level0_weights } from "./shader_fluid_mg_level0_weights.js";
15
+ import { shader_fluid_mg_residual_restrict } from "./shader_fluid_mg_residual_restrict.js";
16
+ import { shader_fluid_mg_smooth } from "./shader_fluid_mg_smooth.js";
17
+ import { FLUID_MG_PASS_STRUCT } from "./FLUID_MG_PASS_STRUCT.js";
18
+
19
+ /**
20
+ * @typedef {import("../../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext} ShadeGPUCommandContext
21
+ */
22
+
23
+ /**
24
+ * A solve's inputs, as {@link GPUFluidPressureSolve#record_v_cycles} documents them, with `cycles` for
25
+ * the V-cycles or `sweeps` for SOR.
26
+ *
27
+ * @typedef {{pressure: GPUBuffer, window: {corner: ArrayLike<number>, corner_storage: ArrayLike<number>, size: ArrayLike<number>, period: ArrayLike<number>}, rhs: GPUBuffer, solid: GPUBuffer, h: number, ghost_pressure: GPUBuffer, cycles?: number, sweeps?: number, level0_weights_ready?: boolean}} FluidPressureSolveInputs
28
+ */
29
+
30
+ const ARM_V_CYCLES = 0;
31
+ const ARM_SOR = 1;
32
+
33
+ const BUFFER_USAGE = () => GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST;
34
+
35
+ /**
36
+ * Bytes between two passes' params in the params buffer: WebGPU's floor for
37
+ * `minUniformBufferOffsetAlignment`, which every device meets.
38
+ * @type {number}
39
+ */
40
+ const PARAMS_STRIDE = 256;
41
+
42
+ /**
43
+ * Pass parameters as {@link FLUID_MG_PASS_STRUCT} lays them out, held so a frame writes into them
44
+ * rather than making new ones.
45
+ */
46
+ function make_pass_values() {
47
+ return {
48
+ corner: new Int32Array(3),
49
+ level: 0,
50
+ size: new Uint32Array(3),
51
+ colours: 0,
52
+ input_corner_storage: new Uint32Array(3),
53
+ sweeps: 0,
54
+ input_period: new Uint32Array(3),
55
+ prolong: 0,
56
+ output_corner_storage: new Uint32Array(3),
57
+ omega: 1,
58
+ output_period: new Uint32Array(3),
59
+ h2: 0,
60
+ coarse_size: new Uint32Array(3),
61
+ edge_weight: 1,
62
+ };
63
+ }
64
+
65
+ /**
66
+ * A window-local, linear buffer as a window: storage corner 0, a period of its size.
67
+ */
68
+ function set_linear(corner_storage, period, size) {
69
+ corner_storage.fill(0);
70
+ period.set(size);
71
+ }
72
+
73
+ /**
74
+ * The GPU fluid's pressure solve for one cascade window (GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md §8):
75
+ * `Σ_f w_f·(p_nb − p) / H² = f` for the physical kinematic pressure `p` (m²/s²), with `f = div/dt`, a
76
+ * solid face closed and the window's edge a Dirichlet boundary at the ghost cell's centre. The
77
+ * cascade's parent's pressure at the ghost cell's centre is the ghost (`chunk_fluid_pressure_ghost`), as
78
+ * the step's pressure ghosts hold it.
79
+ *
80
+ * It owns its level transients, sized by the window and made when the window's size or the device
81
+ * changes, never per frame. Each `record_*` adds one graph pass that dispatches everything; each
82
+ * `encode_*` dispatches the same into the encoder of a pass the caller records itself:
83
+ *
84
+ * - **Weights**: the finest level's words are written by `shader_fluid_mac_divergence` when fused into a
85
+ * cascade step (`level0_weights_ready: true`) or by `shader_fluid_mg_level0_weights` when standalone;
86
+ * each coarse level's words are written by `shader_fluid_mg_residual_restrict` as the V-cycle descends.
87
+ * - **{@link record_v_cycles}**, V-cycles of `3L − 2` dispatches over `L` levels: on each level above
88
+ * the coarsest a smoother of {@link FLUID_MG_SMOOTH_SWEEPS} sweeps and a residual + restriction +
89
+ * coarse weights; the coarsest level in one workgroup; then each level back up, one smoother whose
90
+ * load prolongs the level below in. At 64×32×64, four levels: 10 dispatches a V-cycle.
91
+ * - **{@link record_sor}**, red-black SOR on the finest level at `sor_optimal_omega`, one colour a
92
+ * dispatch: `2K` dispatches for `K` sweeps.
93
+ *
94
+ * A smoother never writes the buffer it reads, so the finest level goes pressure → scratch → pressure.
95
+ *
96
+ * Non-finite pressure, error and RHS are read as 0, and {@link nonfinite_counter} adds one for each such
97
+ * value a pass's owning invocation read. It is never reset.
98
+ *
99
+ * The window's edge is a Dirichlet boundary at the ghost cell's centre: the cascade's parent's
100
+ * pressure there, read from `ghost_pressure`, the step's pressure ghosts (`shader_fluid_cascade_ghosts`);
101
+ * the far field, a parent with no window, reads 0.
102
+ */
103
+ export class GPUFluidPressureSolve {
104
+ /**
105
+ * Offer the compute pipelines the multigrid and SOR pressure solvers dispatch — see
106
+ * {@link PipelineEnquiry}.
107
+ *
108
+ * @param {import("../../../device/pipeline/PipelineEnquiry.js").PipelineEnquiry} enquiry
109
+ */
110
+ static enquirePipelines(enquiry) {
111
+ enquiry.compute.push(
112
+ shader_fluid_mg_smooth.pipeline,
113
+ shader_fluid_mg_residual_restrict.pipeline,
114
+ shader_fluid_mg_coarsest.pipeline,
115
+ shader_fluid_mg_level0_weights.pipeline,
116
+ shader_fluid_mg_coarse_weights.pipeline,
117
+ );
118
+ }
119
+
120
+ /** @type {GPUDevice|null} */
121
+ #device = null;
122
+ /** @type {number[][]} the cells of every level, finest first */
123
+ #sizes = [];
124
+ /**
125
+ * Per level: its weight words; below the finest its RHS (`f`), its error after the up leg (`a`,
126
+ * zeroed by the restriction) and after the down leg (`b`).
127
+ * @type {{words: GPUBuffer, f: GPUBuffer|null, a: GPUBuffer|null, b: GPUBuffer|null, groups: number[]}[]}
128
+ */
129
+ #levels = [];
130
+ /** @type {GPUBuffer|null} the finest level after the down leg */
131
+ #scratch = null;
132
+ /** @type {GPUBuffer|null} */
133
+ #nonfinite = null;
134
+ /** @type {GPUBuffer|null} every pass's params, one {@link PARAMS_STRIDE} slot each */
135
+ #params = null;
136
+ /** @type {ArrayBuffer|null} the bytes {@link #params} is written from */
137
+ #params_data = null;
138
+ /** @type {{values: object, binding: GPUBufferBinding}[]} the slots, in the buffer's order */
139
+ #slots = [];
140
+
141
+ /** Held params and bindings, one per dispatch the solve can make. */
142
+ #weights = [];
143
+ #down = [];
144
+ #restrict = [];
145
+ #up = [];
146
+ #coarsest = null;
147
+ #sor = [];
148
+
149
+ /**
150
+ * One `u32`: how many non-finite values the solves recorded so far read and zeroed. Made with the
151
+ * level transients.
152
+ * @returns {GPUBuffer|null}
153
+ */
154
+ get nonfinite_counter() {
155
+ return this.#nonfinite;
156
+ }
157
+
158
+ /**
159
+ * Ensure level transients exist for `size` on `device` and return the finest level's weight words
160
+ * buffer so `shader_fluid_mac_divergence` can write it directly.
161
+ *
162
+ * @param {GPUDevice} device
163
+ * @param {ArrayLike<number>} size
164
+ * @returns {GPUBuffer}
165
+ */
166
+ level0_words(device, size) {
167
+ this.#configure(device, size);
168
+
169
+ return this.#levels[0].words;
170
+ }
171
+
172
+ /**
173
+ * Record V-cycles into a pass of `graph`. Nothing is recorded for 0 cycles.
174
+ *
175
+ * @param {object} options
176
+ * @param {import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} options.graph
177
+ * @param {GPUBuffer} options.pressure the cascade's pressure, read as the warm start and written with
178
+ * the answer: `f32` at each storage cell of `window`, x fastest
179
+ * @param {{corner: ArrayLike<number>, corner_storage: ArrayLike<number>, size: ArrayLike<number>, period: ArrayLike<number>}} options.window
180
+ * where the pressure is stored, as {@link GRID_TILE_WINDOW_STRUCT}; its size is the solve's cells,
181
+ * powers of two
182
+ * @param {GPUBuffer} options.rhs `f = div/dt` per cell, window-local and linear
183
+ * @param {GPUBuffer} options.solid solid flags per cell (bit 0), window-local and linear
184
+ * @param {number} options.h the cell's size, metres
185
+ * @param {GPUBuffer} options.ghost_pressure the finest level's Dirichlet ghosts (`chunk_fluid_pressure_ghost`):
186
+ * the parent's pressure at every cell one past the window, `array<f32>` over the window grown by
187
+ * one cell on every side, x fastest, as `shader_fluid_cascade_ghosts` writes it
188
+ * @param {number} options.cycles V-cycles
189
+ */
190
+ record_v_cycles({ graph, pressure, window, rhs, solid, h, ghost_pressure, cycles }) {
191
+ assert.isNonNegativeInteger(cycles, 'cycles');
192
+
193
+ this.#record(graph, ARM_V_CYCLES, cycles, { pressure, window, rhs, solid, h, ghost_pressure });
194
+ }
195
+
196
+ /**
197
+ * {@link record_v_cycles} into `encoder` itself, for a caller whose own frame-graph pass records
198
+ * the solve among its other dispatches, as a cascade's step does. Nothing is encoded for 0 cycles.
199
+ * `inputs` is read, not kept.
200
+ *
201
+ * @param {ShadeGPUCommandContext} encoder the encoder a frame-graph pass's callback was handed
202
+ * @param {FluidPressureSolveInputs} inputs
203
+ */
204
+ encode_v_cycles(encoder, inputs) {
205
+ assert.defined(encoder, 'encoder');
206
+ assert.isNonNegativeInteger(inputs.cycles, 'cycles');
207
+
208
+ if (this.#validate(inputs.cycles, inputs)) {
209
+ this.#encode(encoder, ARM_V_CYCLES, inputs.cycles, inputs);
210
+ }
211
+ }
212
+
213
+ /**
214
+ * {@link record_sor} into `encoder` itself, as {@link encode_v_cycles}. Nothing is encoded for 0
215
+ * sweeps.
216
+ *
217
+ * @param {ShadeGPUCommandContext} encoder
218
+ * @param {FluidPressureSolveInputs} inputs
219
+ */
220
+ encode_sor(encoder, inputs) {
221
+ assert.defined(encoder, 'encoder');
222
+ assert.isNonNegativeInteger(inputs.sweeps, 'sweeps');
223
+
224
+ if (this.#validate(inputs.sweeps, inputs)) {
225
+ this.#encode(encoder, ARM_SOR, inputs.sweeps, inputs);
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Record red-black SOR sweeps on the finest level into a pass of `graph`, one colour a dispatch.
231
+ * Nothing is recorded for 0 sweeps.
232
+ *
233
+ * @param {object} options
234
+ * @param {import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} options.graph
235
+ * @param {GPUBuffer} options.pressure as {@link record_v_cycles}
236
+ * @param {{corner: ArrayLike<number>, corner_storage: ArrayLike<number>, size: ArrayLike<number>, period: ArrayLike<number>}} options.window
237
+ * @param {GPUBuffer} options.rhs
238
+ * @param {GPUBuffer} options.solid
239
+ * @param {number} options.h
240
+ * @param {GPUBuffer} options.ghost_pressure
241
+ * @param {number} options.sweeps red-black sweeps
242
+ */
243
+ record_sor({ graph, pressure, window, rhs, solid, h, ghost_pressure, sweeps }) {
244
+ assert.isNonNegativeInteger(sweeps, 'sweeps');
245
+
246
+ this.#record(graph, ARM_SOR, sweeps, { pressure, window, rhs, solid, h, ghost_pressure });
247
+ }
248
+
249
+ #record(graph, arm, count, inputs) {
250
+ assert.defined(graph, 'graph');
251
+
252
+ if (!this.#validate(count, inputs)) {
253
+ return;
254
+ }
255
+
256
+ const pass = graph.add('Fluid / pressure solve', { solve: this, arm, count, inputs }, GPUFluidPressureSolve.#execute);
257
+
258
+ pass.make_side_effect();
259
+ }
260
+
261
+ /**
262
+ * Check a solve's inputs.
263
+ *
264
+ * @param {number} count
265
+ * @param {FluidPressureSolveInputs} inputs
266
+ * @returns {boolean} whether there is anything to encode
267
+ */
268
+ #validate(count, { pressure, window, rhs, solid, h, ghost_pressure }) {
269
+ assert.defined(pressure, 'pressure');
270
+ assert.defined(rhs, 'rhs');
271
+ assert.defined(solid, 'solid');
272
+ assert.defined(ghost_pressure, 'ghost_pressure');
273
+ assert.notEqual(ghost_pressure, pressure, 'the ghosts are not the cascade\'s pressure');
274
+ assert.isFinite(h, 'h');
275
+ assert.greaterThan(h, 0, 'h');
276
+
277
+ const { size, period } = window;
278
+ const cells = size[0] * size[1] * size[2];
279
+
280
+ for (let axis = 0; axis < 3; axis++) {
281
+ assert.isPositiveInteger(size[axis], 'window.size');
282
+ assert.ok((size[axis] & (size[axis] - 1)) === 0, 'window.size is a power of two');
283
+ assert.lessThanOrEqual(size[axis], period[axis], 'window.size within its period');
284
+ }
285
+
286
+ assert.greaterThanOrEqual(pressure.size, 4 * period[0] * period[1] * period[2], 'pressure covers the period');
287
+ assert.greaterThanOrEqual(rhs.size, 4 * cells, 'rhs covers the window');
288
+ assert.greaterThanOrEqual(solid.size, 4 * cells, 'solid covers the window');
289
+
290
+ return count > 0;
291
+ }
292
+
293
+ /**
294
+ * @param {{solve: GPUFluidPressureSolve, arm: number, count: number, inputs: FluidPressureSolveInputs}} data
295
+ * @param {*} _resources
296
+ * @param {{encoder: ShadeGPUCommandContext}} context
297
+ */
298
+ static #execute(data, _resources, context) {
299
+ data.solve.#encode(context.encoder, data.arm, data.count, data.inputs);
300
+ }
301
+
302
+ /**
303
+ * @param {ShadeGPUCommandContext} encoder
304
+ * @param {number} arm
305
+ * @param {number} count
306
+ * @param {FluidPressureSolveInputs} inputs
307
+ */
308
+ #encode(encoder, arm, count, inputs) {
309
+ this.#configure(encoder.device, inputs.window.size);
310
+ this.#prepare(encoder, inputs);
311
+
312
+ const last = this.#levels.length - 1;
313
+
314
+ if (!inputs.level0_weights_ready) {
315
+ dispatch_levelled(encoder, shader_fluid_mg_level0_weights, this.#weights[0], this.#levels[0].groups);
316
+ }
317
+
318
+ if (arm === ARM_SOR) {
319
+ for (let k = 0; k < count; k++) {
320
+ dispatch_smooth(encoder, this.#sor[0], this.#levels[0].groups);
321
+ dispatch_smooth(encoder, this.#sor[1], this.#levels[0].groups);
322
+ }
323
+
324
+ return;
325
+ }
326
+
327
+ for (let cycle = 0; cycle < count; cycle++) {
328
+ for (let l = 0; l < last; l++) {
329
+ dispatch_smooth(encoder, this.#down[l], this.#levels[l].groups);
330
+ dispatch_levelled(encoder, shader_fluid_mg_residual_restrict, this.#restrict[l], this.#levels[l].groups);
331
+ }
332
+
333
+ shader_fluid_mg_coarsest.dispatch({ encoder, bindings: this.#coarsest });
334
+
335
+ for (let l = last - 1; l >= 0; l--) {
336
+ dispatch_smooth(encoder, this.#up[l], this.#levels[l].groups);
337
+ }
338
+ }
339
+ }
340
+
341
+ /**
342
+ * The level transients for a window of `size` cells on `device`, made anew only when either
343
+ * changed.
344
+ */
345
+ #configure(device, size) {
346
+ const sizes = this.#sizes;
347
+
348
+ if (device === this.#device && sizes.length > 0
349
+ && sizes[0][0] === size[0] && sizes[0][1] === size[1] && sizes[0][2] === size[2]) {
350
+ return;
351
+ }
352
+
353
+ this.dispose();
354
+
355
+ this.#device = device;
356
+ this.#sizes = fluid_mg_level_sizes(size[0], size[1], size[2]);
357
+
358
+ const buffer = (label, cells) => device.createBuffer({ label: `Fluid / pressure / ${label}`, size: 4 * cells, usage: BUFFER_USAGE() });
359
+
360
+ this.#levels = this.#sizes.map((s, l) => {
361
+ const cells = s[0] * s[1] * s[2];
362
+ const coarse = l > 0;
363
+
364
+ return {
365
+ words: buffer(`level ${l} weights`, cells),
366
+ f: coarse ? buffer(`level ${l} rhs`, cells) : null,
367
+ a: coarse ? buffer(`level ${l} error`, cells) : null,
368
+ b: coarse ? buffer(`level ${l} error (down)`, cells) : null,
369
+ groups: s.map((n, axis) => Math.ceil(n / GRID_TILE_SIZE[axis])),
370
+ };
371
+ });
372
+
373
+ const s0 = this.#sizes[0];
374
+ this.#scratch = buffer('scratch', s0[0] * s0[1] * s0[2]);
375
+ this.#nonfinite = buffer('non-finite count', 1);
376
+
377
+ const last = this.#levels.length - 1;
378
+
379
+ // weights per level; down, restrict and up per level above the coarsest; the coarsest; SOR red and black
380
+ const slot_count = this.#levels.length + 3 * last + 1 + 2;
381
+
382
+ this.#params = device.createBuffer({ label: 'Fluid / pressure / pass params', size: slot_count * PARAMS_STRIDE, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST });
383
+ this.#params_data = new ArrayBuffer(slot_count * PARAMS_STRIDE);
384
+ this.#slots = [];
385
+
386
+ const params = () => {
387
+ const slot = { values: make_pass_values(), binding: { buffer: this.#params, offset: this.#slots.length * PARAMS_STRIDE, size: FLUID_MG_PASS_STRUCT.size } };
388
+
389
+ this.#slots.push(slot);
390
+
391
+ return slot;
392
+ };
393
+
394
+ const smoother = () => ({ slot: params(), params: null, input: null, output: null, rhs: null, words: null, coarse: null, nonfinite: this.#nonfinite, ghost_pressure: null });
395
+
396
+ this.#weights = this.#levels.map(() => ({ slot: params(), params: null, solid: null, fine_words: null, words: null }));
397
+ this.#down = [];
398
+ this.#restrict = [];
399
+ this.#up = [];
400
+
401
+ for (let l = 0; l < last; l++) {
402
+ this.#down.push(smoother());
403
+ this.#restrict.push({ slot: params(), params: null, input: null, rhs: null, words: null, coarse_rhs: null, coarse_error: null, coarse_words: null, nonfinite: this.#nonfinite, ghost_pressure: null });
404
+ this.#up.push(smoother());
405
+ }
406
+
407
+ this.#coarsest = { slot: params(), params: null, values: null, rhs: null, words: null, nonfinite: this.#nonfinite, ghost_pressure: null };
408
+ this.#sor = [smoother(), smoother()];
409
+
410
+ for (const bindings of [...this.#weights, ...this.#down, ...this.#restrict, ...this.#up, this.#coarsest, ...this.#sor]) {
411
+ bindings.params = bindings.slot.binding;
412
+ }
413
+
414
+ assert.equal(this.#slots.length, slot_count, 'slots');
415
+ }
416
+
417
+ /**
418
+ * Every held pass's params and bindings for this solve's inputs.
419
+ *
420
+ * @param {ShadeGPUCommandContext} encoder
421
+ * @param {FluidPressureSolveInputs} inputs
422
+ */
423
+ #prepare(encoder, { pressure, window, rhs, solid, h, ghost_pressure }) {
424
+ const sizes = this.#sizes;
425
+ const levels = this.#levels;
426
+ const last = levels.length - 1;
427
+
428
+ for (let l = 0; l <= last; l++) {
429
+ const weights = this.#weights[l];
430
+
431
+ set_level_params(weights.slot.values, sizes, l, h, window);
432
+ weights.solid = solid;
433
+ weights.fine_words = l === 0 ? null : levels[l - 1].words;
434
+ weights.words = levels[l].words;
435
+ }
436
+
437
+ for (let l = 0; l < last; l++) {
438
+ const level_rhs = l === 0 ? rhs : levels[l].f;
439
+
440
+ const down = this.#down[l];
441
+
442
+ set_level_params(down.slot.values, sizes, l, h, window);
443
+ down.input = l === 0 ? pressure : levels[l].a;
444
+ down.output = l === 0 ? this.#scratch : levels[l].b;
445
+ down.rhs = level_rhs;
446
+ down.words = levels[l].words;
447
+ // a smoother that prolongs nothing binds the RHS in its place, read-only twice
448
+ down.coarse = level_rhs;
449
+
450
+ if (l === 0) {
451
+ set_pressure_input(down.slot.values, window);
452
+ }
453
+
454
+ const restrict = this.#restrict[l];
455
+
456
+ set_level_params(restrict.slot.values, sizes, l, h, window);
457
+ restrict.input = down.output;
458
+ restrict.rhs = level_rhs;
459
+ restrict.words = levels[l].words;
460
+ restrict.coarse_rhs = levels[l + 1].f;
461
+ restrict.coarse_error = levels[l + 1].a;
462
+ restrict.coarse_words = levels[l + 1].words;
463
+
464
+ const up = this.#up[l];
465
+
466
+ set_level_params(up.slot.values, sizes, l, h, window);
467
+ up.slot.values.prolong = 1;
468
+ up.input = down.output;
469
+ up.output = l === 0 ? pressure : levels[l].a;
470
+ up.rhs = level_rhs;
471
+ up.words = levels[l].words;
472
+ up.coarse = levels[l + 1].a;
473
+
474
+ if (l === 0) {
475
+ set_pressure_output(up.slot.values, window);
476
+ }
477
+ }
478
+
479
+ const coarsest = this.#coarsest;
480
+
481
+ set_level_params(coarsest.slot.values, sizes, last, h, window);
482
+ coarsest.values = last === 0 ? pressure : levels[last].a;
483
+ coarsest.rhs = last === 0 ? rhs : levels[last].f;
484
+ coarsest.words = levels[last].words;
485
+
486
+ if (last === 0) {
487
+ set_pressure_input(coarsest.slot.values, window);
488
+ }
489
+
490
+ const omega = sor_optimal_omega(sizes[0][0], sizes[0][1], sizes[0][2]);
491
+
492
+ for (let colour = 0; colour < 2; colour++) {
493
+ const sor = this.#sor[colour];
494
+
495
+ set_level_params(sor.slot.values, sizes, 0, h, window);
496
+ sor.slot.values.colours = colour === 0 ? FluidMGColour.Red : FluidMGColour.Black;
497
+ sor.slot.values.sweeps = 1;
498
+ sor.slot.values.omega = omega;
499
+ sor.input = colour === 0 ? pressure : this.#scratch;
500
+ sor.output = colour === 0 ? this.#scratch : pressure;
501
+ sor.rhs = rhs;
502
+ sor.words = levels[0].words;
503
+ sor.coarse = rhs;
504
+
505
+ if (colour === 0) {
506
+ set_pressure_input(sor.slot.values, window);
507
+ } else {
508
+ set_pressure_output(sor.slot.values, window);
509
+ }
510
+ }
511
+
512
+ // the step's ghosts, for the finest level's Dirichlet boundary; the passes below it bind them unread
513
+ for (const bindings of [...this.#down, ...this.#restrict, ...this.#up, this.#coarsest, ...this.#sor]) {
514
+ bindings.ghost_pressure = ghost_pressure;
515
+ }
516
+
517
+ // every slot in one write, recorded in order with the passes, so a second solve recorded into
518
+ // the same frame cannot overwrite what this one's passes read
519
+ const slots = this.#slots;
520
+
521
+ for (let i = 0; i < slots.length; i++) {
522
+ write_gpu_typed_buffer(slots[i].values, FLUID_MG_PASS_STRUCT, this.#params_data, i * PARAMS_STRIDE);
523
+ }
524
+
525
+ encoder.writeBuffer(this.#params, 0, this.#params_data, 0, this.#params_data.byteLength);
526
+ }
527
+
528
+ /**
529
+ * Release the level transients and the counter.
530
+ */
531
+ dispose() {
532
+ for (const level of this.#levels) {
533
+ level.words.destroy();
534
+ level.f?.destroy();
535
+ level.a?.destroy();
536
+ level.b?.destroy();
537
+ }
538
+
539
+ this.#scratch?.destroy();
540
+ this.#nonfinite?.destroy();
541
+ this.#params?.destroy();
542
+
543
+ this.#levels = [];
544
+ this.#sizes = [];
545
+ this.#scratch = null;
546
+ this.#nonfinite = null;
547
+ this.#params = null;
548
+ this.#params_data = null;
549
+ this.#slots = [];
550
+ this.#device = null;
551
+ this.#weights = [];
552
+ this.#down = [];
553
+ this.#restrict = [];
554
+ this.#up = [];
555
+ this.#coarsest = null;
556
+ this.#sor = [];
557
+ }
558
+ }
559
+
560
+ /**
561
+ * A level's pass params as a smoother of both colours at ω = 1 with no prolongation, reading and
562
+ * writing window-local linear buffers.
563
+ *
564
+ * @param {object} params
565
+ * @param {number[][]} sizes every level's cells
566
+ * @param {number} l the level
567
+ * @param {number} h the finest cell's size
568
+ * @param {{corner: ArrayLike<number>}} window
569
+ */
570
+ function set_level_params(params, sizes, l, h, window) {
571
+ const size = sizes[l];
572
+ const H = h * 2 ** l;
573
+
574
+ params.size.set(size);
575
+ params.level = l;
576
+ params.h2 = H * H;
577
+ params.edge_weight = fluid_mg_edge_weight(l);
578
+ set_linear(params.input_corner_storage, params.input_period, size);
579
+ set_linear(params.output_corner_storage, params.output_period, size);
580
+
581
+ if (l === 0) {
582
+ params.corner.set(window.corner);
583
+ } else {
584
+ params.corner.fill(0);
585
+ }
586
+
587
+ if (l + 1 < sizes.length) {
588
+ params.coarse_size.set(sizes[l + 1]);
589
+ } else {
590
+ params.coarse_size.fill(0);
591
+ }
592
+
593
+ params.colours = FluidMGColour.Both;
594
+ params.sweeps = FLUID_MG_SMOOTH_SWEEPS;
595
+ params.omega = 1;
596
+ params.prolong = 0;
597
+ }
598
+
599
+ /**
600
+ * The pass reads the cascade's pressure, through its window.
601
+ */
602
+ function set_pressure_input(params, window) {
603
+ params.input_corner_storage.set(window.corner_storage);
604
+ params.input_period.set(window.period);
605
+ }
606
+
607
+ /**
608
+ * The pass writes the cascade's pressure, through its window.
609
+ */
610
+ function set_pressure_output(params, window) {
611
+ params.output_corner_storage.set(window.corner_storage);
612
+ params.output_period.set(window.period);
613
+ }
614
+
615
+ /**
616
+ * A pass over a level, one workgroup per 8×8×4 cells.
617
+ */
618
+ function dispatch_levelled(encoder, shader, bindings, groups) {
619
+ shader.dispatch({ encoder, bindings, group_count_x: groups[0], group_count_y: groups[1], group_count_z: groups[2] });
620
+ }
621
+
622
+ /**
623
+ * A smoother pass. Both colours against one frozen halo diverge for ω > 1, and one colour twice in a
624
+ * dispatch is not a sweep.
625
+ */
626
+ function dispatch_smooth(encoder, bindings, groups) {
627
+ const params = bindings.slot.values;
628
+
629
+ assert.ok(params.colours !== FluidMGColour.Both || params.omega === 1, `both colours in one smoother dispatch need omega = 1, not ${params.omega}`);
630
+ assert.ok(params.colours === FluidMGColour.Both || params.sweeps === 1, 'one colour is one sweep a dispatch');
631
+ assert.lessThanOrEqual(params.sweeps, FLUID_MG_SMOOTH_SWEEP_LIMIT, 'sweeps');
632
+
633
+ dispatch_levelled(encoder, shader_fluid_mg_smooth, bindings, groups);
634
+ }