@woosh/meep-engine 3.41.0 → 3.42.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 (344) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/editor/selection/editor_pick.d.ts.map +1 -1
  4. package/editor/selection/editor_pick.js +2 -1
  5. package/editor/tools/TransformToolV2.d.ts.map +1 -1
  6. package/editor/tools/TransformToolV2.js +7 -1
  7. package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
  8. package/editor/tools/engine/ToolEngine.js +10 -0
  9. package/editor/tools/v2/TransformControls.d.ts.map +1 -1
  10. package/editor/tools/v2/TransformControls.js +7 -20
  11. package/editor/tools/v2/handle/gizmo_drag_plane.d.ts.map +1 -1
  12. package/editor/tools/v2/handle/gizmo_drag_plane.js +4 -0
  13. package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
  14. package/editor/view/ecs/EntityEditor.js +3 -1
  15. package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
  16. package/editor/view/node-graph/NodeGraphView.js +28 -12
  17. package/package.json +1 -1
  18. package/samples/engine/first-scene/index.html +4 -1
  19. package/samples/engine/first-scene/main.js +14 -9
  20. package/src/core/binary/utf8/utf8_decode.js +3 -3
  21. package/src/core/bvh2/binary/2/bvh32_query_user_data_ray.d.ts.map +1 -1
  22. package/src/core/bvh2/binary/2/bvh32_query_user_data_ray.js +112 -113
  23. package/src/core/bvh2/bvh3/ebvh_nodes_sort_sah_local4.js +2 -2
  24. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +14 -14
  25. package/src/core/collection/RingBuffer.d.ts.map +1 -1
  26. package/src/core/collection/RingBuffer.js +4 -0
  27. package/src/core/collection/array/array_contains_duplicates.d.ts.map +1 -1
  28. package/src/core/collection/array/array_contains_duplicates.js +2 -1
  29. package/src/core/collection/array/array_copy.d.ts.map +1 -1
  30. package/src/core/collection/array/array_copy.js +23 -0
  31. package/src/core/collection/array/typed/sparse_typed_array_hash.d.ts +2 -2
  32. package/src/core/collection/array/typed/sparse_typed_array_hash.d.ts.map +1 -1
  33. package/src/core/collection/array/typed/sparse_typed_array_hash.js +9 -8
  34. package/src/core/collection/array/typed/uint16_array_hash.d.ts +2 -2
  35. package/src/core/collection/array/typed/uint16_array_hash.js +4 -4
  36. package/src/core/collection/array/typed/uint8_array_hash.d.ts +2 -2
  37. package/src/core/collection/array/typed/uint8_array_hash.js +4 -4
  38. package/src/core/collection/array/weightedRandomFromArray.d.ts +1 -0
  39. package/src/core/collection/array/weightedRandomFromArray.d.ts.map +1 -1
  40. package/src/core/collection/array/weightedRandomFromArray.js +9 -1
  41. package/src/core/collection/heap/BinaryHeap.d.ts.map +1 -1
  42. package/src/core/collection/heap/BinaryHeap.js +4 -0
  43. package/src/core/collection/list/List.d.ts.map +1 -1
  44. package/src/core/collection/list/List.js +4 -2
  45. package/src/core/collection/set/ArraySet.js +1 -1
  46. package/src/core/collection/set/HashSet.d.ts +1 -1
  47. package/src/core/collection/set/HashSet.d.ts.map +1 -1
  48. package/src/core/collection/set/HashSet.js +4 -2
  49. package/src/core/debug/matchers/IsEqual.js +1 -1
  50. package/src/core/debug/matchers/IsIterableContaining.d.ts.map +1 -1
  51. package/src/core/debug/matchers/IsIterableContaining.js +61 -74
  52. package/src/core/events/signal/SignalBinding.js +101 -101
  53. package/src/core/function/extractFunctionBody.js +13 -13
  54. package/src/core/function/objectsEqual.d.ts.map +1 -1
  55. package/src/core/function/objectsEqual.js +15 -14
  56. package/src/core/geom/3d/frustum/frustum3_compute_closest_point.d.ts.map +1 -1
  57. package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +207 -172
  58. package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts.map +1 -1
  59. package/src/core/geom/3d/gjk/gjk_epa_penetration.js +5 -4
  60. package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.d.ts.map +1 -1
  61. package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.js +8 -2
  62. package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts +20 -0
  63. package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts.map +1 -1
  64. package/src/core/geom/3d/triangle/tri3_ray_intersection.js +182 -0
  65. package/src/core/math/noise/curl_noise_3dt.d.ts.map +1 -1
  66. package/src/core/math/noise/curl_noise_3dt.js +20 -10
  67. package/src/core/math/noise/sdnoise.d.ts +16 -5
  68. package/src/core/math/noise/sdnoise.d.ts.map +1 -1
  69. package/src/core/math/noise/sdnoise.js +43 -9
  70. package/src/core/math/spline/spline3_hermite_nearest_point.d.ts +16 -1
  71. package/src/core/math/spline/spline3_hermite_nearest_point.d.ts.map +1 -1
  72. package/src/core/math/spline/spline3_hermite_nearest_point.js +44 -3
  73. package/src/engine/Engine.d.ts.map +1 -1
  74. package/src/engine/Engine.js +6 -2
  75. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +9 -0
  76. package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
  77. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +26 -0
  78. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
  79. package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +2 -0
  80. package/src/engine/control/first-person/abilities/AbilitySet.d.ts.map +1 -1
  81. package/src/engine/control/first-person/abilities/AbilitySet.js +9 -0
  82. package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
  83. package/src/engine/control/first-person/abilities/LedgeGrab.js +6 -0
  84. package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
  85. package/src/engine/control/first-person/abilities/Slide.js +1 -0
  86. package/src/engine/control/first-person/abilities/WallRun.d.ts +4 -1
  87. package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -1
  88. package/src/engine/control/first-person/abilities/WallRun.js +13 -1
  89. package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts +1 -1
  90. package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts.map +1 -1
  91. package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.js +6 -5
  92. package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts +3 -3
  93. package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts.map +1 -1
  94. package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.js +9 -8
  95. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
  96. package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +13 -4
  97. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  98. package/src/engine/ecs/EntityManager.js +18 -15
  99. package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.d.ts.map +1 -1
  100. package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js +2 -1
  101. package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescriptionTable.d.ts.map +1 -1
  102. package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescriptionTable.js +1 -0
  103. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
  104. package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +3 -0
  105. package/src/engine/ecs/parent/EntityNode.js +3 -3
  106. package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
  107. package/src/engine/ecs/speaker/VoiceSystem.js +4 -0
  108. package/src/engine/ecs/storage/BinaryBufferDeSerializer.js +5 -5
  109. package/src/engine/ecs/storage/binary/BinarySerializationRegistry.d.ts.map +1 -1
  110. package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +3 -2
  111. package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.js +1 -1
  112. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.d.ts.map +1 -1
  113. package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +8 -1
  114. package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
  115. package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +8 -0
  116. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
  117. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +5 -2
  118. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.d.ts.map +1 -1
  119. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +3 -0
  120. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts.map +1 -1
  121. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +1 -0
  122. package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +1 -1
  123. package/src/engine/ecs/terrain/tiles/TerrainTile.d.ts.map +1 -1
  124. package/src/engine/ecs/terrain/tiles/TerrainTile.js +13 -1
  125. package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
  126. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +1 -0
  127. package/src/engine/graphics/ecs/light/LightSerializationAdapter.d.ts.map +1 -1
  128. package/src/engine/graphics/ecs/light/LightSerializationAdapter.js +13 -1
  129. package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.d.ts +5 -0
  130. package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.d.ts.map +1 -0
  131. package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.js +27 -0
  132. package/src/engine/graphics/ecs/water/Water.d.ts +11 -0
  133. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  134. package/src/engine/graphics/ecs/water/Water.js +17 -0
  135. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
  136. package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +3 -1
  137. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.d.ts +9 -0
  138. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.d.ts.map +1 -0
  139. package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.js +24 -0
  140. package/src/engine/graphics/ecs/water/WaterWaves.d.ts +30 -9
  141. package/src/engine/graphics/ecs/water/WaterWaves.d.ts.map +1 -1
  142. package/src/engine/graphics/ecs/water/WaterWaves.js +41 -9
  143. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts.map +1 -1
  144. package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.js +30 -26
  145. package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.d.ts +6 -0
  146. package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.d.ts.map +1 -0
  147. package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.js +22 -0
  148. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.d.ts.map +1 -1
  149. package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.js +3 -5
  150. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.d.ts +20 -0
  151. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.d.ts.map +1 -1
  152. package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +36 -0
  153. package/src/engine/graphics3/WaterSystem.d.ts +12 -3
  154. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  155. package/src/engine/graphics3/WaterSystem.js +78 -46
  156. package/src/engine/input/devices/PointerDevice.d.ts.map +1 -1
  157. package/src/engine/input/devices/PointerDevice.js +29 -4
  158. package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts +1 -0
  159. package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts.map +1 -1
  160. package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.js +14 -3
  161. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  162. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.d.ts.map +1 -1
  163. package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +12 -3
  164. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.d.ts.map +1 -1
  165. package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +8 -2
  166. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts +19 -0
  167. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts.map +1 -1
  168. package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +41 -2
  169. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.d.ts.map +1 -1
  170. package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +9 -3
  171. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.d.ts.map +1 -1
  172. package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +26 -10
  173. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +3 -6
  174. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
  175. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +22 -9
  176. package/src/engine/ui/DraggableAspect.d.ts +5 -1
  177. package/src/engine/ui/DraggableAspect.d.ts.map +1 -1
  178. package/src/engine/ui/DraggableAspect.js +61 -4
  179. package/src/format/image/avif/heif/ItemProperty.d.ts +1 -1
  180. package/src/format/image/avif/heif/ItemProperty.js +1 -1
  181. package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts.map +1 -1
  182. package/src/format/image/png/chunk/png_chunk_decode_iTXt.js +17 -6
  183. package/src/shade/device/mock/SoftwareGPUDevice.d.ts +2 -4
  184. package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
  185. package/src/shade/device/mock/SoftwareGPUDevice.js +20 -13
  186. package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts.map +1 -1
  187. package/src/shade/playground/skinned_blas_refit/sweep_views.js +2 -1
  188. package/src/shade/playground/water_refraction/README.md +10 -11
  189. package/src/shade/playground/water_refraction/main.js +9 -8
  190. package/src/shade/playground/water_refraction/world.js +1 -1
  191. package/src/shade/playground/water_surface/README.md +43 -0
  192. package/src/shade/playground/water_surface/index.html +30 -0
  193. package/src/shade/playground/water_surface/main.d.ts +2 -0
  194. package/src/shade/playground/water_surface/main.d.ts.map +1 -0
  195. package/src/shade/playground/water_surface/main.js +154 -0
  196. package/src/shade/playground/water_surface/world.d.ts +44 -0
  197. package/src/shade/playground/water_surface/world.d.ts.map +1 -0
  198. package/src/shade/playground/water_surface/world.js +160 -0
  199. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  200. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +5 -0
  201. package/src/shade/renderer/binding/BufferBinding.js +2 -2
  202. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  203. package/src/shade/renderer/buffer/table/GPUTypedTable.js +5 -3
  204. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts +10 -3
  205. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts.map +1 -1
  206. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.js +17 -9
  207. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +3 -0
  208. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +1 -1
  209. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +16 -0
  210. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +2 -0
  211. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +1 -1
  212. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +11 -0
  213. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +1 -1
  214. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
  215. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +9 -3
  216. package/src/shade/renderer/geometry/util/geometry_simplify_redundant_topology.d.ts.map +1 -1
  217. package/src/shade/renderer/geometry/util/geometry_simplify_redundant_topology.js +2 -2
  218. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  219. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +85 -0
  220. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  221. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +12 -6
  222. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
  223. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +7 -0
  224. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
  225. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +3 -1
  226. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  227. package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +2 -1
  228. package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.d.ts +13 -0
  229. package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.d.ts.map +1 -0
  230. package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.js +42 -0
  231. package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.d.ts +15 -0
  232. package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.d.ts.map +1 -0
  233. package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.js +39 -0
  234. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  235. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +2 -0
  236. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +15 -0
  237. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  238. package/src/shade/renderer/water/GPUWaterRenderer.js +110 -30
  239. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +16 -8
  240. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -1
  241. package/src/shade/renderer/water/medium/GPUWaterBodies.js +31 -26
  242. package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts +0 -1
  243. package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts.map +1 -1
  244. package/src/shade/renderer/water/medium/shader_water_meshlets.js +1 -39
  245. package/src/shade/renderer/water/shader_water.d.ts +0 -1
  246. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  247. package/src/shade/renderer/water/shader_water.js +201 -72
  248. package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts +70 -0
  249. package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts.map +1 -0
  250. package/src/shade/renderer/water/surface/GPUWaterSurface.js +199 -0
  251. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts +5 -2
  252. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts.map +1 -1
  253. package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.js +32 -12
  254. package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts +22 -0
  255. package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts.map +1 -0
  256. package/src/shade/renderer/water/surface/chunk_water_surface_sample.js +217 -0
  257. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +65 -11
  258. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -1
  259. package/src/shade/renderer/water/surface/graph_water_tessellation.js +163 -28
  260. package/src/shade/renderer/water/surface/lod/WATER_LOD.d.ts +51 -0
  261. package/src/shade/renderer/water/surface/lod/WATER_LOD.d.ts.map +1 -0
  262. package/src/shade/renderer/water/surface/lod/WATER_LOD.js +95 -0
  263. package/src/shade/renderer/water/surface/lod/chunk_water_lod.d.ts +10 -0
  264. package/src/shade/renderer/water/surface/lod/chunk_water_lod.d.ts.map +1 -0
  265. package/src/shade/renderer/water/surface/lod/chunk_water_lod.js +127 -0
  266. package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.d.ts +17 -0
  267. package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.d.ts.map +1 -0
  268. package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.js +246 -0
  269. package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts +118 -0
  270. package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts.map +1 -0
  271. package/src/shade/renderer/water/surface/lod/shader_water_lod.js +727 -0
  272. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts +60 -0
  273. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -0
  274. package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +228 -0
  275. package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts +17 -0
  276. package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts.map +1 -0
  277. package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.js +49 -0
  278. package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts +22 -0
  279. package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts.map +1 -0
  280. package/src/shade/renderer/water/surface/particles/chunk_water_particle.js +143 -0
  281. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts +15 -0
  282. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts.map +1 -0
  283. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.js +121 -0
  284. package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts +83 -0
  285. package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts.map +1 -0
  286. package/src/shade/renderer/water/surface/particles/shader_water_particles.js +502 -0
  287. package/src/shade/renderer/water/surface/shader_water_cull.d.ts +42 -0
  288. package/src/shade/renderer/water/surface/shader_water_cull.d.ts.map +1 -0
  289. package/src/shade/renderer/water/surface/shader_water_cull.js +269 -0
  290. package/src/shade/renderer/water/surface/shader_water_waves_combine.d.ts +15 -0
  291. package/src/shade/renderer/water/surface/shader_water_waves_combine.d.ts.map +1 -0
  292. package/src/shade/renderer/water/surface/shader_water_waves_combine.js +73 -0
  293. package/src/shade/renderer/water/surface/shader_water_waves_derive.d.ts +11 -0
  294. package/src/shade/renderer/water/surface/shader_water_waves_derive.d.ts.map +1 -0
  295. package/src/shade/renderer/water/surface/shader_water_waves_derive.js +78 -0
  296. package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.d.ts +35 -0
  297. package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.d.ts.map +1 -0
  298. package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.js +103 -0
  299. package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.d.ts +22 -0
  300. package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.d.ts.map +1 -0
  301. package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.js +57 -0
  302. package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.d.ts +22 -0
  303. package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.d.ts.map +1 -0
  304. package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.js +25 -0
  305. package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.d.ts +14 -0
  306. package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.d.ts.map +1 -0
  307. package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.js +48 -0
  308. package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.d.ts +23 -0
  309. package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.d.ts.map +1 -0
  310. package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.js +169 -0
  311. package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.d.ts +26 -0
  312. package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.d.ts.map +1 -0
  313. package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.js +161 -0
  314. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
  315. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +18 -6
  316. package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
  317. package/src/shade/wgsl/emulator/WGSLLanguageCore.js +11 -3
  318. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +0 -28
  319. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +0 -1
  320. package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +0 -86
  321. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.d.ts +0 -29
  322. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.d.ts.map +0 -1
  323. package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +0 -44
  324. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +0 -4
  325. package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +0 -1
  326. package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +0 -32
  327. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +0 -25
  328. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +0 -1
  329. package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +0 -128
  330. package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts +0 -9
  331. package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts.map +0 -1
  332. package/src/shade/renderer/water/surface/chunk_water_tessellation.js +0 -144
  333. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +0 -13
  334. package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +0 -1
  335. package/src/shade/renderer/water/surface/chunk_water_wave.js +0 -40
  336. package/src/shade/renderer/water/surface/shader_water_tiles.d.ts +0 -4
  337. package/src/shade/renderer/water/surface/shader_water_tiles.d.ts.map +0 -1
  338. package/src/shade/renderer/water/surface/shader_water_tiles.js +0 -123
  339. package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts +0 -6
  340. package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts.map +0 -1
  341. package/src/shade/renderer/water/surface/shader_water_wave_batch.js +0 -85
  342. package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts +0 -6
  343. package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts.map +0 -1
  344. package/src/shade/renderer/water/surface/shader_water_wave_domain.js +0 -83
@@ -1,15 +1,23 @@
1
- /** GPU work over the scene's water table; no per-body render commands or transient body uniforms. */
1
+ /** A view's water bodies: which the view draws and how, and their meshlets in row order. */
2
2
  export class GPUWaterBodies {
3
- record({ graph, scene }: {
4
- graph: any;
5
- scene: any;
6
- }): void;
7
- /** Ordered prefix expansion preserves equal-depth ownership, including after holes and reuse. */
8
- meshlets({ graph, view, camera, meshlet_capacity }: {
3
+ /**
4
+ * Draw culling over the frame's visible list (WATER_SURFACE_DISTANCE_LOD_PROPOSAL_2026_10_06.md §4.3), each drawn
5
+ * body's lattice frame at its row, and the drawn bodies' meshlets. The expansion is an ordered prefix scan over the
6
+ * rows, which preserves equal-depth ownership, including after holes and reuse.
7
+ *
8
+ * @returns {{meshlets: number, lod_bodies: number}} resources
9
+ */
10
+ meshlets({ graph, view, camera, meshlet_capacity, visible_rows, lod, steep }: {
9
11
  graph: any;
10
12
  view: any;
11
13
  camera: any;
12
14
  meshlet_capacity: any;
13
- }): any;
15
+ visible_rows: any;
16
+ lod: any;
17
+ steep: any;
18
+ }): {
19
+ meshlets: number;
20
+ lod_bodies: number;
21
+ };
14
22
  }
15
23
  //# sourceMappingURL=GPUWaterBodies.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"GPUWaterBodies.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/water/medium/GPUWaterBodies.js"],"names":[],"mappings":"AAUA,qGAAqG;AACrG;IACI;;;aASC;IAED,iGAAiG;IACjG;;;;;YAsCC;CACJ"}
1
+ {"version":3,"file":"GPUWaterBodies.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/water/medium/GPUWaterBodies.js"],"names":[],"mappings":"AAcA,4FAA4F;AAC5F;IACI;;;;;;OAMG;IACH;;;;;;;;QAFa;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAC,CA8ClD;CACJ"}
@@ -1,42 +1,47 @@
1
1
  import { BufferResourceDescriptor } from "../../../device/graph/BufferResourceDescriptor.js";
2
2
  import { round_up } from "../../../../core/math/round_up.js";
3
3
  import { graph_prefix_scan_csdldf } from "../../gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js";
4
- import { INDIRECT_COMPUTE_COMMAND_DESCRIPTOR } from "../../gpu_primitive/indirect/IndirectComputeCommandBuildPass.js";
4
+ import {
5
+ build_indirect_compute_dispatch_command,
6
+ INDIRECT_COMPUTE_COMMAND_DESCRIPTOR
7
+ } from "../../gpu_primitive/indirect/IndirectComputeCommandBuildPass.js";
5
8
  import { shader_prefix_sum_to_command } from "../../rasterize/expand/mesh/shader_prefix_sum_to_command.js";
6
9
  import { shader_increment_count_by_prefix_sum } from "../../rasterize/expand/mesh/shader_increment_count_by_prefix_sum.js";
7
10
  import { graph_compute_pass } from "../../shader/graph/compute/graph_compute_pass.js";
8
- import { shader_water_meshlet_counts, shader_water_meshlets_expand } from "./shader_water_meshlets.js";
9
- import { shader_water_bodies_derive, WORKGROUP_SIZE } from "./shader_water_bodies_derive.js";
11
+ import { shader_water_meshlets_expand } from "./shader_water_meshlets.js";
12
+ import { shader_water_lod_bodies } from "../surface/lod/shader_water_lod.js";
13
+ import { WATER_LOD_BODY_STRUCT, WATER_LOD_VIEW_STRUCT } from "../surface/lod/WATER_LOD.js";
10
14
 
11
- /** GPU work over the scene's water table; no per-body render commands or transient body uniforms. */
15
+ /** A view's water bodies: which the view draws and how, and their meshlets in row order. */
12
16
  export class GPUWaterBodies {
13
- record({ graph, scene }) {
14
- const table = scene.scene_database.get('water_bodies');
15
- const groups = table.dispatch_group_count(WORKGROUP_SIZE);
16
- if (groups === 0) { return; }
17
- const pass = graph.add('Water / bodies derive', {}, (_data, _resources, { encoder }) => {
18
- shader_water_bodies_derive.dispatch({ encoder, bindings: { scene_database: scene.scene_database_buffer },
19
- group_count_x: Math.min(groups, 65535), group_count_y: Math.ceil(groups / 65535) });
20
- });
21
- pass.make_side_effect();
22
- }
23
-
24
- /** Ordered prefix expansion preserves equal-depth ownership, including after holes and reuse. */
25
- meshlets({ graph, view, camera, meshlet_capacity }) {
17
+ /**
18
+ * Draw culling over the frame's visible list (WATER_SURFACE_DISTANCE_LOD_PROPOSAL_2026_10_06.md §4.3), each drawn
19
+ * body's lattice frame at its row, and the drawn bodies' meshlets. The expansion is an ordered prefix scan over the
20
+ * rows, which preserves equal-depth ownership, including after holes and reuse.
21
+ *
22
+ * @returns {{meshlets: number, lod_bodies: number}} resources
23
+ */
24
+ meshlets({ graph, view, camera, meshlet_capacity, visible_rows, lod, steep }) {
26
25
  const scene = view.scene, table = scene.scene_database.get('water_bodies');
27
- const capacity = table.element_capacity;
26
+ const capacity = Math.max(table.element_capacity, 1);
28
27
  const data = {};
29
- const pass = graph.add('Water / meshlet counts', data, (d, resources, { encoder }) => {
30
- const groups = table.dispatch_group_count(64);
31
- shader_water_meshlet_counts.dispatch({ encoder, bindings: {
32
- capacity: round_up(capacity, 4), camera: resources.get(d.camera), scene_database: scene.scene_database_buffer,
33
- geometries: scene.geometries.buffer_metadata, counts: resources.get(d.counts),
34
- }, group_count_x: Math.min(groups, 65535), group_count_y: Math.ceil(groups / 65535) });
28
+ const pass = graph.add('Water / lod bodies', data, (d, resources, { encoder }) => {
29
+ const command = resources.get(d.command);
30
+ build_indirect_compute_dispatch_command(64, encoder, visible_rows, 0, command, 0, true);
31
+ shader_water_lod_bodies.dispatchIndirect({ encoder, command, bindings: {
32
+ capacity: round_up(capacity, 4), lod: encoder.allocateTransientValueBuffer(WATER_LOD_VIEW_STRUCT, lod),
33
+ camera: resources.get(d.camera), scene_database: scene.scene_database_buffer,
34
+ geometries: scene.geometries.buffer_metadata, visible_rows, steep,
35
+ lod_bodies: resources.get(d.lod_bodies), counts: resources.get(d.counts),
36
+ } });
35
37
  });
36
38
  data.camera = pass.read(camera);
37
39
  // The shared scan reads vec4u blocks, including the padded final block.
38
40
  const bytes = 16 + round_up(capacity, 4) * 4;
39
41
  data.counts = pass.create('counts', BufferResourceDescriptor.from(bytes, GPUBufferUsage.STORAGE, 0, bytes));
42
+ data.command = pass.create('Water lod bodies command', INDIRECT_COMPUTE_COMMAND_DESCRIPTOR);
43
+ data.lod_bodies = pass.create('Water lod bodies', BufferResourceDescriptor.from(capacity * WATER_LOD_BODY_STRUCT.size,
44
+ GPUBufferUsage.STORAGE));
40
45
  const sums = graph_prefix_scan_csdldf({ graph, data: data.counts, element_max_count: capacity });
41
46
  const command_data = {};
42
47
  const command_pass = graph.add('Water / expand command', command_data, (d, resources, { encoder }) => {
@@ -55,10 +60,10 @@ export class GPUWaterBodies {
55
60
  });
56
61
  expand_data.command = expand.read(command);
57
62
  expand_data.sums = expand.read(sums);
58
- expand_data.output = expand.create('meshlets', BufferResourceDescriptor.from(16 + meshlet_capacity * 8,
63
+ expand_data.output = expand.create('meshlets', BufferResourceDescriptor.from(16 + Math.max(meshlet_capacity, 1) * 8,
59
64
  GPUBufferUsage.STORAGE, 0, 16));
60
65
  const { output } = graph_compute_pass({ graph, shader: shader_increment_count_by_prefix_sum,
61
66
  bindings: { prefix_sums: sums, output: expand_data.output } });
62
- return output;
67
+ return { meshlets: output, lod_bodies: data.lod_bodies };
63
68
  }
64
69
  }
@@ -1,4 +1,3 @@
1
- export const shader_water_meshlet_counts: ComputeShader;
2
1
  export const shader_water_meshlets_expand: ComputeShader;
3
2
  import { ComputeShader } from "../../shader/ComputeShader.js";
4
3
  //# sourceMappingURL=shader_water_meshlets.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shader_water_meshlets.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/water/medium/shader_water_meshlets.js"],"names":[],"mappings":"AAyBA,wDAsBkH;AAOlH,yDAcsF;8BAhExD,+BAA+B"}
1
+ {"version":3,"file":"shader_water_meshlets.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/water/medium/shader_water_meshlets.js"],"names":[],"mappings":"AAgBA,yDAcsF;8BA3BxD,+BAA+B"}
@@ -1,4 +1,3 @@
1
- import { CAMERA_UNIFORM_STRUCT } from "../../camera/CAMERA_UNIFORM_STRUCT.js";
2
1
  import { GEOMETRY_METADATA_STRUCT } from "../../geometry/GEOMETRY_METADATA_STRUCT.js";
3
2
  import { MESHLET_COLLECTION_STRUCT } from "../../rasterize/MESHLET_COLLECTION_STRUCT.js";
4
3
  import { COLLECTION_U32_STRUCT_READ } from "../../gpu_primitive/COLLECTION_U32_STRUCT_READ.js";
@@ -6,46 +5,9 @@ import { ComputeShader } from "../../shader/ComputeShader.js";
6
5
  import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
7
6
  import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
8
7
  import { WebGPUArray } from "../../shader/type/WebGPUArray.js";
9
- import { chunk_scene_database_access } from "../../scene/chunk_scene_database_access.js";
10
8
  import { chunk_folded_workgroup_index } from "../../shader/chunk/grid/chunk_folded_workgroup_index.js";
11
9
  import { chunk_lower_bound_branchless } from "../../shader/chunk/math/chunk_lower_bound_branchless.js";
12
- import { WATER_BODIES_TABLE as table, WATER_WAVES_TABLE } from "./chunk_water_bodies.js";
13
- import { chunk_aabb3_project } from "../../shader/chunk/geometry/aabb3/chunk_aabb3_project.js";
14
- import { chunk_aabb3_intersects_frustum } from "../../shader/chunk/geometry/aabb3/chunk_aabb3_intersects_frustum.js";
15
- import { chunk_array_to_aabb3 } from "../../shader/chunk/geometry/aabb3/chunk_array_to_aabb3.js";
16
- import { chunk_mat4_linear_determinant } from "../../shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js";
17
- import { u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
18
-
19
- const pg = table.page_iterator_symbol_prefix(64);
20
- const counts_resources = new ShaderResourceSetDescriptor();
21
- counts_resources.setImmediate('capacity', u32);
22
- counts_resources.createGroup().addUniform('camera', CAMERA_UNIFORM_STRUCT)
23
- .addStorageBuffer('scene_database', WebGPUArray.u32)
24
- .addStorageBuffer('geometries', WebGPUArray.from(GEOMETRY_METADATA_STRUCT))
25
- .addStorageBuffer('counts', COLLECTION_U32_STRUCT_READ, true);
26
- export const shader_water_meshlet_counts = ComputeShader.from({ label: 'Water / meshlet counts', resources: counts_resources,
27
- body: CodeChunk.from(/* language=WGSL */ `
28
- @compute @workgroup_size(64)
29
- fn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_index) lane: u32) {
30
- if (all(group == vec3u(0)) && lane == 0u) { counts.count = capacity; }
31
- if (!${pg}_setup(&scene_database, folded_workgroup_index(group), lane)) { return; }
32
- let slot = ${pg}_slot_in_page(lane);
33
- if (slot >= ${pg}_ELEMENTS_PER_PAGE || !${pg}_is_occupied(slot)) { return; }
34
- let body = ${pg}_read(&scene_database, slot);
35
- let geometry = geometries[body.geometry];
36
- let node = scene_read_node(&scene_database, body.node);
37
- if (abs(mat4_linear_determinant(node.global)) < 1e-20) { return; }
38
- var box = geometry.bounding_box;
39
- if (body.wave != ~0u) {
40
- let displacement = ${WATER_WAVES_TABLE.marshalling_method_read}(&scene_database, body.wave).displacement_bounds;
41
- box[1] += displacement.x; box[4] += displacement.y;
42
- }
43
- let bounds = aabb3_project(array_to_aabb3(box), node.global);
44
- if (!aabb3_intersects_frustum(bounds, camera.frustum)) { return; }
45
- counts.elements[${pg}_slot_to_index(slot)] = geometry.meshlets_count;
46
- }
47
- `, [table.chunk_iterate(64), WATER_WAVES_TABLE.chunk_read, chunk_scene_database_access, chunk_folded_workgroup_index,
48
- chunk_aabb3_project, chunk_array_to_aabb3, chunk_aabb3_intersects_frustum, chunk_mat4_linear_determinant]) });
10
+ import { WATER_BODIES_TABLE as table } from "./chunk_water_bodies.js";
49
11
 
50
12
  const expand_resources = new ShaderResourceSetDescriptor();
51
13
  expand_resources.createGroup().addStorageBuffer('prefix_sums', COLLECTION_U32_STRUCT_READ)
@@ -1,7 +1,6 @@
1
1
  export function create_water_pipeline(): RenderPipelineDescriptor;
2
2
  export function create_water_finalize_pipeline_ibl(): RenderPipelineDescriptor;
3
3
  export function create_water_finalize_pipeline_brick4(): RenderPipelineDescriptor;
4
- export const WATER_GRID_STRUCT: WebGPUStruct;
5
4
  export const WATER_TRACE_STRUCT: WebGPUStruct;
6
5
  /** Optics under IBL (and LPV, which has no transparent variant): the lights and the environment. */
7
6
  export const WATER_FINALIZE_RESOURCES_IBL: ShaderResourceSetDescriptor;
@@ -1 +1 @@
1
- {"version":3,"file":"shader_water.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/water/shader_water.js"],"names":[],"mappings":"AAueA,kEAAuH;AACvH,+EAA+J;AAC/J,kFAAqK;AAlcrK,6CAAqF;AAErF,8CAKwB;AA0BxB,oGAAoG;AACpG,uEAAkE;AAGlE,gGAAgG;AAChG,0EAAqE;AAkHrE,+CA+EmB;AA2LnB,4CAAmG;AACnG,kFAAkF;AAClF,yDAC8G;AAC9G,oCAAoC;AACpC,4DACoH;AACpH,0DAAyC;yCA9cA,8DAA8D;6BAmB1E,gCAAgC;4CAJjB,mDAAmD;0BADrE,iCAAiC;iCAE1B,+BAA+B"}
1
+ {"version":3,"file":"shader_water.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/water/shader_water.js"],"names":[],"mappings":"AAwmBA,kEAAuH;AACvH,+EAA+J;AAC/J,kFAAqK;AA/jBrK,8CAKwB;AAiCxB,oGAAoG;AACpG,uEAAkE;AAGlE,gGAAgG;AAChG,0EAAqE;AAqOrE,+CA+EmB;AA6LnB,4CAAmG;AACnG,kFAAkF;AAClF,yDAC8G;AAC9G,oCAAoC;AACpC,4DACoH;AACpH,0DAAyC;yCA5kBA,8DAA8D;6BAmB1E,gCAAgC;4CAJjB,mDAAmD;0BADrE,iCAAiC;iCAE1B,+BAA+B"}
@@ -25,8 +25,12 @@ import { chunk_water_gi_brick4, chunk_water_gi_ibl } from "./chunk_water_gi.js";
25
25
  import { WATER_BODIES_TABLE, WATER_WAVES_TABLE } from "./medium/chunk_water_bodies.js";
26
26
  import { MESHLET_METADATA_STRUCT } from "../geometry/meshlet/encoding/MESHLET_METADATA_STRUCT.js";
27
27
  import { COLLECTION_U32_STRUCT_READ } from "../gpu_primitive/COLLECTION_U32_STRUCT_READ.js";
28
- import { chunk_lower_bound_branchless } from "../shader/chunk/math/chunk_lower_bound_branchless.js";
29
- import { chunk_water_tessellation, WATER_TILE_STRUCT, WATER_TESSELLATION_STRUCT, WATER_TILE_CELLS } from "./surface/chunk_water_tessellation.js";
28
+ import { chunk_water_surface_sample } from "./surface/chunk_water_surface_sample.js";
29
+ import { WATER_PARTICLE_ENTRY_STRUCT } from "./surface/particles/WATER_PARTICLE_STRUCT.js";
30
+ import { WATER_LOD_BODY_STRUCT, WATER_LOD_VIEW_STRUCT, WaterLodMode } from "./surface/lod/WATER_LOD.js";
31
+ import { chunk_water_lod_candidate, chunk_water_lod_layout, WATER_LOD_BOUNDARY_FIRST_VERTEX, WATER_LOD_CANDIDATE_STRUCT,
32
+ WATER_LOD_CELL_LOCAL_BITS, WATER_LOD_LIMITS_STRUCT, WATER_LOD_PATCH_FIRST_VERTEX, WATER_LOD_PATCH_SIDE, WATER_LOD_POLYGON_WORDS,
33
+ WATER_LOD_TRIANGLE_FIRST_VERTEX, WaterLodList } from "./surface/lod/shader_water_lod.js";
30
34
  import { chunk_read_meshlet_vertex_by_draw_index } from "../shader/chunk/geometry/meshlet/chunk_read_meshlet_vertex_by_draw_index.js";
31
35
  import { chunk_scene_database_access } from "../scene/chunk_scene_database_access.js";
32
36
  import { chunk_mat3_inverse } from "../shader/chunk/geometry/mat3/chunk_mat3_inverse.js";
@@ -37,8 +41,6 @@ import { chunk_water_optics } from "./optics/chunk_water_optics.js";
37
41
  import { chunk_water_receiver_radiance, chunk_water_receiver_texel, chunk_water_refraction_confidence,
38
42
  chunk_water_transport, WATER_LEVEL_TOLERANCE_DECLARATION } from './chunk_water_transport.js';
39
43
 
40
- export const WATER_GRID_STRUCT = WebGPUStruct.from({ resolution: u32 }, 'WaterGrid');
41
-
42
44
  export const WATER_TRACE_STRUCT = WebGPUStruct.from({
43
45
  max_distance: f32, distance_tolerance: f32, max_updates: u32, fallback_steps: u32,
44
46
  mode: u32, reflections: u32,
@@ -56,13 +58,20 @@ function water_resources(finalize = false) {
56
58
  .addTexture("tHZB")
57
59
  .addTexture("tScene")
58
60
  .addStorageBuffer('scene_database', WebGPUArray.u32)
59
- .addStorageBuffer('water_tiles', WebGPUArray.from(WATER_TILE_STRUCT))
60
- .addStorageBuffer('water_tile_sums', COLLECTION_U32_STRUCT_READ)
61
- .addUniform('tessellation', WATER_TESSELLATION_STRUCT)
61
+ // the view's lattice (WATER_SURFACE_DISTANCE_LOD_PROPOSAL_2026_10_06.md §4.3)
62
+ .addUniform('lod', WATER_LOD_VIEW_STRUCT)
63
+ .addUniform('lod_limits', WATER_LOD_LIMITS_STRUCT)
64
+ .addStorageBuffer('water_lod_bodies', WebGPUArray.from(WATER_LOD_BODY_STRUCT))
65
+ .addStorageBuffer('water_lod_candidates', WebGPUArray.from(WATER_LOD_CANDIDATE_STRUCT))
66
+ .addStorageBuffer('water_lod_cells', WebGPUArray.u32)
62
67
  .addStorageBuffer('meshlet_metadata', WebGPUArray.from(MESHLET_METADATA_STRUCT))
63
68
  .addStorageBuffer('meshlet_data', WebGPUArray.from(f32))
64
- .addStorageBuffer('wave_state', WebGPUArray.from(f32))
65
- .addUniform('wave_grid', WATER_GRID_STRUCT);
69
+ // the surface interface: the spectral bands and the frame's binned wave particles
70
+ .addTexture('water_band_displacement', 'float', '2d-array')
71
+ .addTexture('water_band_derivatives', 'float', '2d-array')
72
+ .addSampler('water_band_sampler')
73
+ .addStorageBuffer('water_particle_offsets', COLLECTION_U32_STRUCT_READ)
74
+ .addStorageBuffer('water_particle_entries', WebGPUArray.from(WATER_PARTICLE_ENTRY_STRUCT));
66
75
  if (finalize) {
67
76
  group.addTexture('receiver').addTexture('trace_metadata').addTexture('visibility_distance');
68
77
  }
@@ -88,79 +97,194 @@ struct WaterVertexOut {
88
97
  // makes the position bit-identical across programs.
89
98
  @invariant @builtin(position) clip: vec4<f32>,
90
99
  @location(0) world: vec3<f32>,
100
+ // the normal of the waves the mesh carries
91
101
  @location(1) normal: vec3<f32>,
92
- // slope variance of the waves the surface's mesh does not resolve
102
+ // slope variance of the wave packets the mesh does not resolve; the bands' is the pixel's to work out
93
103
  @location(2) unresolved_variance: f32,
94
104
  @location(3) @interpolate(flat) body_index: u32,
95
105
  @location(4) rest_normal: vec3<f32>,
106
+ // where on the body's rest surface this is, metric local xz, and the footprint its waves were filtered to there:
107
+ // what the pixel samples the bands the mesh could not carry at; 0 where no waves move the surface
108
+ @location(5) metric: vec2<f32>,
109
+ @location(6) footprint: f32,
110
+ @location(7) @interpolate(flat) wave: u32,
111
+ // the body's metric x and z axes in the world, to tilt the normal along
112
+ @location(8) @interpolate(flat) axis_x: vec3<f32>,
113
+ @location(9) @interpolate(flat) axis_z: vec3<f32>,
114
+ // the metres a pixel spans on the rest surface: its distance times its angle, stretched where the surface is seen
115
+ // at a slant (the mean of its two axes)
116
+ @location(10) pixel_footprint: f32,
117
+ }
118
+
119
+ fn water_vertex_out_none(body_index: u32) -> WaterVertexOut {
120
+ return WaterVertexOut(vec4f(0, 0, 0, 1), vec3f(0), vec3f(0, 1, 0), 0.0, body_index, vec3f(0, 1, 0), vec2f(0), 0.0, 0u,
121
+ vec3f(1, 0, 0), vec3f(0, 0, 1), 0.0);
122
+ }
123
+
124
+ struct WaterPixelSurface {
125
+ normal: vec3<f32>,
126
+ variance: f32,
127
+ }
128
+
129
+ // The surface a pixel shades: the mesh's normal tilted by the bands the pixel resolves and the mesh could not carry;
130
+ // the slope variance of the bands neither resolves, and of the packets the mesh did not draw.
131
+ fn water_pixel_surface(in: WaterVertexOut, pixel_footprint: f32) -> WaterPixelSurface {
132
+ if (!(in.footprint > 0.0)) { return WaterPixelSurface(in.normal, in.unresolved_variance); }
133
+ let detail = water_surface_detail(in.wave, in.metric, in.footprint, pixel_footprint);
134
+ let normal = normalize(in.normal - detail.slope.x * in.axis_x - detail.slope.y * in.axis_z);
135
+ return WaterPixelSurface(normal, in.unresolved_variance + detail.variance);
136
+ }
137
+
138
+ // The rest point a vertex of the view's lattice stands for, and how it is drawn.
139
+ struct WaterLodVertex {
140
+ // local units
141
+ rest: WaterLodRest,
142
+ // metric local xz, where the waves are sampled
143
+ metric: vec2<f32>,
144
+ // metres between samples the waves are filtered to; 0 for a vertex the waves do not move
145
+ footprint: f32,
146
+ present: bool,
96
147
  }
97
148
 
98
- fn water_wave_height(row: u32, cell: vec2i) -> f32 {
99
- let n = i32(wave_grid.resolution);
100
- let c = clamp(cell, vec2i(0), vec2i(n - 1));
101
- return wave_state[row * u32(n * n) + u32(c.y * n + c.x)];
149
+ const WATER_LOD_QUAD: array<u32, 6> = array<u32, 6>(0u, 1u, 2u, 0u, 2u, 3u);
150
+ const WATER_LOD_QUAD_REVERSED: array<u32, 6> = array<u32, 6>(0u, 2u, 1u, 0u, 3u, 2u);
151
+
152
+ // A lattice point of a cell drawn whole: on the triangle's surface, along an authored edge where it lies on one.
153
+ fn water_lod_whole_point(level: u32, point: vec2<i32>, frame: ${WATER_LOD_BODY_STRUCT.wgsl_ref}, tri: WaterLodTriangle) -> WaterLodVertex {
154
+ var out: WaterLodVertex;
155
+ let p = water_lod_lattice_point(lod, frame, level, point);
156
+ out.rest = water_lod_rest(tri, water_lod_edge_bits(tri, p), p, frame.scale);
157
+ out.metric = p;
158
+ out.footprint = water_lod_footprint(lod, frame, level, p);
159
+ out.present = true;
160
+ return out;
161
+ }
162
+
163
+ fn water_lod_vertex(list: u32, corner: u32, candidate: ${WATER_LOD_CANDIDATE_STRUCT.wgsl_ref}, place: u32,
164
+ frame: ${WATER_LOD_BODY_STRUCT.wgsl_ref}, tri: WaterLodTriangle, mirrored: bool, polygon: vec2<u32>) -> WaterLodVertex {
165
+ var out: WaterLodVertex;
166
+ let packed = candidate.packed;
167
+ if (list == ${WaterLodList.Triangle}u) {
168
+ // the authored triangle itself, wound as authored
169
+ let k = select(corner, 2u - corner, mirrored);
170
+ out.rest = WaterLodRest(tri.local[k], tri.normal[k]);
171
+ out.metric = tri.p[k];
172
+ out.present = true;
173
+ return out;
174
+ }
175
+ let level = water_lod_candidate_level(packed);
176
+ if (place >= 256u) { return out; }
177
+ // the cell's place in its node: quadrant · 64 + row · 8 + column
178
+ let cell = water_lod_quadrant_origin(candidate.node, place >> 6u) + vec2<i32>(i32(place & 7u), i32((place >> 3u) & 7u));
179
+ // a cell's corners run counter-clockwise from x towards z; the authored triangle may run the other way, and a
180
+ // mirroring placement turns both
181
+ let reversed = tri.ccw == mirrored;
182
+ if (list == ${WaterLodList.Patch}u) {
183
+ // the patch's indices run its points row by row; read across instead, they turn every cell the other way
184
+ var grid = vec2<i32>(i32(corner % ${WATER_LOD_PATCH_SIDE}u), i32(corner / ${WATER_LOD_PATCH_SIDE}u));
185
+ if (reversed) { grid = grid.yx; }
186
+ return water_lod_whole_point(level, cell + grid, frame, tri);
187
+ }
188
+ if (list == ${WaterLodList.Interior}u) {
189
+ let k = select(WATER_LOD_QUAD[corner], WATER_LOD_QUAD_REVERSED[corner], reversed);
190
+ return water_lod_whole_point(level, cell + vec2<i32>(i32(k == 1u || k == 2u), i32(k >= 2u)), frame, tri);
191
+ }
192
+ // a boundary cell's point, rebuilt from what the cells pass found it to lie on
193
+ let corners = array<vec2<f32>, 4>(
194
+ water_lod_lattice_point(lod, frame, level, cell),
195
+ water_lod_lattice_point(lod, frame, level, cell + vec2<i32>(1, 0)),
196
+ water_lod_lattice_point(lod, frame, level, cell + vec2<i32>(1, 1)),
197
+ water_lod_lattice_point(lod, frame, level, cell + vec2<i32>(0, 1)));
198
+ let fan = corner / 3u;
199
+ let at = corner % 3u;
200
+ var index = 0u;
201
+ if (at != 0u) { index = select(fan + at, fan + 3u - at, reversed); }
202
+ let bits = water_lod_polygon_bits(polygon, index);
203
+ out.metric = water_lod_point(tri, corners, bits);
204
+ out.rest = water_lod_rest(tri, bits, out.metric, frame.scale);
205
+ out.footprint = water_lod_footprint(lod, frame, level, out.metric);
206
+ out.present = true;
207
+ return out;
102
208
  }
103
209
 
104
- // One instance per occupied view tile and source triangle. All overlap candidates survive
105
- // classification; receiver depth selects the nearest displaced interface.
210
+ // One instance a record of one of the view's lists; the list is told by the draw's first vertex.
106
211
  @vertex
107
212
  fn vs_main(@builtin(vertex_index) vi: u32, @builtin(instance_index) ii: u32) -> WaterVertexOut {
108
- let row = lower_bound_branchless(&water_tile_sums.elements, ii + 1u, water_tile_sums.count);
109
- let definition = water_tiles[row];
110
- let body = ${WATER_BODIES_TABLE.marshalling_method_read}(&scene_database, definition.body);
111
- let header = meshlet_metadata[definition.meshlet];
213
+ var list = ${WaterLodList.Interior}u;
214
+ var corner = vi;
215
+ var candidate: ${WATER_LOD_CANDIDATE_STRUCT.wgsl_ref};
216
+ var place = 0u;
217
+ var polygon = vec2<u32>(0u);
218
+ if (vi >= ${WATER_LOD_PATCH_FIRST_VERTEX}u) {
219
+ // a patch: its candidate and its quadrant's first cell; the index names the point
220
+ list = ${WaterLodList.Patch}u; corner = vi - ${WATER_LOD_PATCH_FIRST_VERTEX}u;
221
+ let record = water_lod_cells[water_lod_patches_base(lod_limits) + ii];
222
+ candidate = water_lod_candidates[record >> ${WATER_LOD_CELL_LOCAL_BITS}u];
223
+ place = record & ${(1 << WATER_LOD_CELL_LOCAL_BITS) - 1}u;
224
+ } else if (vi >= ${WATER_LOD_TRIANGLE_FIRST_VERTEX}u) {
225
+ // a triangle drawn as itself: its meshlet, then its body row and its triangle
226
+ list = ${WaterLodList.Triangle}u; corner = vi - ${WATER_LOD_TRIANGLE_FIRST_VERTEX}u;
227
+ let at = water_lod_triangles_base(lod_limits) + 2u * ii;
228
+ candidate.meshlet = water_lod_cells[at];
229
+ candidate.body = water_lod_cells[at + 1u] >> 7u;
230
+ candidate.packed = water_lod_cells[at + 1u] & 127u;
231
+ } else {
232
+ var base = 0u;
233
+ if (vi >= ${WATER_LOD_BOUNDARY_FIRST_VERTEX}u) {
234
+ list = ${WaterLodList.Boundary}u; corner = vi - ${WATER_LOD_BOUNDARY_FIRST_VERTEX}u; base = lod_limits.interior;
235
+ // a fan triangle past the cell's polygon draws nothing, decided before anything else is read
236
+ let at = water_lod_polygons_base(lod_limits) + ${WATER_LOD_POLYGON_WORDS}u * ii;
237
+ polygon = vec2<u32>(water_lod_cells[at], water_lod_cells[at + 1u]);
238
+ if (water_lod_polygon_count(polygon) < corner / 3u + 3u) { return water_vertex_out_none(0u); }
239
+ }
240
+ let record = water_lod_cells[base + ii];
241
+ candidate = water_lod_candidates[record >> ${WATER_LOD_CELL_LOCAL_BITS}u];
242
+ place = record & ${(1 << WATER_LOD_CELL_LOCAL_BITS) - 1}u;
243
+ }
244
+ let body = ${WATER_BODIES_TABLE.marshalling_method_read}(&scene_database, candidate.body);
245
+ let frame = water_lod_bodies[candidate.body];
112
246
  let node = scene_read_node(&scene_database, body.node);
113
- var previous = 0u;
114
- if (row > 0u) { previous = water_tile_sums.elements[row - 1u]; }
115
- let patch_index = ii - previous;
116
- let tile = definition.origin + vec2i(i32(patch_index % definition.size.x), i32(patch_index / definition.size.x));
117
- let cell = vi / 18u;
118
- let lattice = tile * ${WATER_TILE_CELLS} + vec2i(i32(cell % ${WATER_TILE_CELLS}u), i32(cell / ${WATER_TILE_CELLS}u));
119
- let pixel = vec2f(lattice) * definition.pixels;
120
- let ndc0 = pixel / tessellation.resolution * vec2f(2,-2) + vec2f(-1,1);
121
- // Form both endpoints from their integer lattice coordinate. Adding the pitch to
122
- // an already rounded pixel coordinate gives neighbouring cells different edges.
123
- let ndc1 = (vec2f(lattice + vec2i(1)) * definition.pixels) / tessellation.resolution * vec2f(2,-2) + vec2f(-1,1);
124
- let a = read_meshlet_vertex_by_draw_index(header, definition.triangle * 3u);
125
- let b = read_meshlet_vertex_by_draw_index(header, definition.triangle * 3u + 1u);
126
- let c = read_meshlet_vertex_by_draw_index(header, definition.triangle * 3u + 2u);
127
- let simulated = body.wave != ~0u;
128
- let ca = tessellation.view_projection_matrix * (node.global * vec4f(water_reference_position(a.position, definition.anchor, simulated), 1));
129
- let cb = tessellation.view_projection_matrix * (node.global * vec4f(water_reference_position(b.position, definition.anchor, simulated), 1));
130
- let cc = tessellation.view_projection_matrix * (node.global * vec4f(water_reference_position(c.position, definition.anchor, simulated), 1));
131
- let polygon = water_cell_polygon(ca, cb, cc, min(ndc0,ndc1), max(ndc0,ndc1));
132
- let fan = (vi % 18u) / 3u;
133
- if (polygon.count < fan + 3u) { return WaterVertexOut(vec4f(0,0,0,1),vec3f(0),vec3f(0,1,0),0,definition.body,vec3f(0,1,0)); }
134
- let corner = select(vi % 3u, 2u - vi % 3u, (node.global_flags & ${NODE3D_GLOBAL_FLAG_MIRRORED}u) != 0u);
135
- var index = 0u;
136
- if (corner != 0u) { index = fan + corner; }
137
- let barycentric = polygon.points[index];
138
- var position = water_corner_position(a.position,b.position,c.position,ca,cb,cc,barycentric,
139
- polygon.boundaries[index],min(ndc0,ndc1),max(ndc0,ndc1));
140
- var normal = normalize(a.normal * barycentric.x + b.normal * barycentric.y + c.normal * barycentric.z);
247
+ let header = meshlet_metadata[candidate.meshlet];
248
+ let triangle = water_lod_candidate_triangle(candidate.packed);
249
+ let a = read_meshlet_vertex_by_draw_index(header, triangle * 3u);
250
+ let b = read_meshlet_vertex_by_draw_index(header, triangle * 3u + 1u);
251
+ let c = read_meshlet_vertex_by_draw_index(header, triangle * 3u + 2u);
252
+ let tri = water_lod_triangle(a.position, b.position, c.position, a.normal, b.normal, c.normal, frame.scale);
253
+ let mirrored = (node.global_flags & ${NODE3D_GLOBAL_FLAG_MIRRORED}u) != 0u;
254
+ let vertex = water_lod_vertex(list, corner, candidate, place, frame, tri, mirrored, polygon);
255
+ if (!vertex.present) { return water_vertex_out_none(candidate.body); }
256
+ var position = vertex.rest.position;
257
+ var normal = normalize(vertex.rest.normal);
141
258
  let rest_normal = normal;
142
259
  var variance = 0.0;
143
- if (body.wave != ~0u) {
144
- let params = ${WATER_WAVES_TABLE.marshalling_method_read}(&scene_database, body.wave);
145
- let spacing = max((params.extent.zw - params.extent.xy) / f32(wave_grid.resolution - 1u), vec2f(1e-20));
146
- let grid = clamp((position.xz - params.extent.xy) / spacing, vec2f(0.0), vec2f(f32(wave_grid.resolution - 1u)));
147
- let cell = vec2i(grid);
148
- let blend = fract(grid);
149
- let h00 = water_wave_height(body.wave, cell);
150
- let h10 = water_wave_height(body.wave, cell + vec2i(1, 0));
151
- let h01 = water_wave_height(body.wave, cell + vec2i(0, 1));
152
- let h11 = water_wave_height(body.wave, cell + vec2i(1, 1));
153
- position.y += mix(mix(h00, h10, blend.x), mix(h01, h11, blend.x), blend.y);
154
- let slope = vec2f(mix(h10 - h00, h11 - h01, blend.y), mix(h01 - h00, h11 - h10, blend.x)) / spacing;
260
+ var footprint = 0.0;
261
+ if (frame.mode != ${WaterLodMode.Hidden}u && any(frame.bounds != vec2<f32>(0.0))) {
262
+ // a vertex the waves do not move carries none of them: its pixels shade what they resolve
263
+ footprint = select(1e30, vertex.footprint, vertex.footprint > 0.0);
264
+ let sum = water_surface_sum(body.wave, vertex.metric, footprint);
265
+ let surface = water_surface_finish(sum, 1.0);
266
+ // the metric frame's normal in the local frame is S·n
267
+ let tilted = surface.normal * frame.scale;
268
+ let slope = -tilted.xz / tilted.y;
269
+ if (vertex.footprint > 0.0) { position += surface.offset / frame.scale; }
155
270
  normal = normalize(vec3f(normal.x - normal.y * slope.x, normal.y, normal.z - normal.y * slope.y));
156
- variance = params.unresolved_variance;
271
+ if (vertex.footprint <= 0.0) { normal = rest_normal; }
272
+ variance = sum.particle_variance;
157
273
  }
158
274
  let world = (node.global * vec4f(position, 1.0)).xyz;
159
275
  let linear = mat3x3f(node.global[0].xyz, node.global[1].xyz, node.global[2].xyz);
160
276
  let inverse_transpose = transpose(mat3_inverse(linear));
161
277
  let normal_ws = normalize(inverse_transpose * normal);
162
278
  let rest_normal_ws = normalize(inverse_transpose * rest_normal);
163
- return WaterVertexOut(camera.view_projection_matrix * vec4f(world, 1.0), world, normal_ws, variance, definition.body,rest_normal_ws);
279
+ // a pixel's footprint on the rest surface: distance times angle (an orthographic pixel's own size) across the view,
280
+ // that over the cosine at which the surface is seen along it; their geometric mean, as anisotropic filtering takes
281
+ let to_eye = camera.view_matrix_inverse[3].xyz - world;
282
+ let orthographic = lod.orthographic != 0u;
283
+ let across = select(length(to_eye) * lod.pixel, lod.pixel, orthographic);
284
+ let view = select(normalize(to_eye), normalize(camera.view_matrix_inverse[2].xyz), orthographic);
285
+ let pixel_footprint = across / sqrt(max(abs(dot(view, rest_normal_ws)), 0.05));
286
+ return WaterVertexOut(camera.view_projection_matrix * vec4f(world, 1.0), world, normal_ws, variance, candidate.body, rest_normal_ws,
287
+ vertex.metric, footprint, body.wave, normalize(node.global[0].xyz), normalize(node.global[2].xyz), pixel_footprint);
164
288
  }
165
289
 
166
290
  // The interface at one water fragment, in view space. Receivers trace through it and optics
@@ -185,9 +309,9 @@ fn water_interface(world: vec3<f32>, surface_normal: vec3<f32>, ior: f32, above:
185
309
  return WaterInterface(origin, normalize(origin), normalize(direction_world_to_view(normal_ws, camera)),
186
310
  max(ior, 1.0), min(max(trace.max_distance, 0.1), camera.clip_near_far.y));
187
311
  }
188
- `, [chunk_direction_world_to_view, WATER_BODIES_TABLE.chunk_read, WATER_WAVES_TABLE.chunk_read,
189
- chunk_read_meshlet_vertex_by_draw_index, chunk_scene_database_access, chunk_mat3_inverse,
190
- chunk_lower_bound_branchless, chunk_water_tessellation]);
312
+ `, [chunk_direction_world_to_view, WATER_BODIES_TABLE.chunk_read, chunk_read_meshlet_vertex_by_draw_index,
313
+ chunk_scene_database_access, chunk_mat3_inverse, chunk_water_lod_candidate, chunk_water_lod_layout,
314
+ WATER_LOD_CANDIDATE_STRUCT.declaration_chunk, chunk_water_surface_sample]);
191
315
 
192
316
  export const chunk_water_screen_ray = CodeChunk.from(/* language=WGSL */ `
193
317
  fn water_hzb_mip_size(depth_size: vec2f, hzb_size: vec2f, mip: i32) -> vec2f {
@@ -287,6 +411,7 @@ fn water_receiver_submerged(uv: vec2f, surface: vec3f, normal: vec3f) -> bool {
287
411
 
288
412
  @fragment
289
413
  fn fs_main(in: WaterVertexOut, @builtin(front_facing) above: bool) -> WaterReceiver {
414
+ let pixel = water_pixel_surface(in, in.pixel_footprint);
290
415
  let water = ${WATER_BODIES_TABLE.marshalling_method_read}(&scene_database, in.body_index);
291
416
  let coord = vec2<i32>(in.clip.xy);
292
417
  let dimensions = vec2f(textureDimensions(tDepth, 0));
@@ -305,7 +430,7 @@ fn fs_main(in: WaterVertexOut, @builtin(front_facing) above: bool) -> WaterRecei
305
430
  water_depth = abs(dot(in.world - (camera.view_matrix_inverse * vec4f(behind_vs, 1.0)).xyz, normalize(in.rest_normal)));
306
431
  }
307
432
  let coverage = smoothstep(0.0, 0.1, water_depth);
308
- let surface = water_interface(in.world, in.normal, water.ior, above);
433
+ let surface = water_interface(in.world, pixel.normal, water.ior, above);
309
434
  let interface_normal = direction_view_to_world(surface.normal, camera);
310
435
  let limit = surface.limit;
311
436
  let direction = refract(surface.incident, surface.normal, 1.0 / surface.ior);
@@ -364,6 +489,7 @@ struct WaterOptics {
364
489
  }
365
490
  @fragment
366
491
  fn fs_main(in: WaterVertexOut, @builtin(front_facing) above: bool) -> WaterOptics {
492
+ let pixel = water_pixel_surface(in, in.pixel_footprint);
367
493
  let water = ${WATER_BODIES_TABLE.marshalling_method_read}(&scene_database, in.body_index);
368
494
  let coord = vec2i(in.clip.xy);
369
495
  let metadata = textureLoad(trace_metadata, coord, 0);
@@ -372,7 +498,7 @@ fn fs_main(in: WaterVertexOut, @builtin(front_facing) above: bool) -> WaterOptic
372
498
  let dimensions = vec2f(textureDimensions(tDepth, 0));
373
499
  let uv = in.clip.xy / dimensions;
374
500
  let receiver_data = textureLoad(receiver, coord, 0);
375
- let surface = water_interface(in.world, in.normal, water.ior, above);
501
+ let surface = water_interface(in.world, pixel.normal, water.ior, above);
376
502
  let interface_normal = direction_view_to_world(surface.normal, camera);
377
503
  let body = water;
378
504
  let origin = surface.origin;
@@ -384,7 +510,7 @@ fn fs_main(in: WaterVertexOut, @builtin(front_facing) above: bool) -> WaterOptic
384
510
  let reflection_direction = reflect(surface.incident, surface.normal);
385
511
  // a reflection that misses on screen falls back to the GI mode, as SSR's resolve does
386
512
  var reflection = water_gi_specular(in.world, direction_view_to_world(surface.normal, camera),
387
- direction_view_to_world(-surface.incident, camera), clamp(sqrt(water.roughness * water.roughness + in.unresolved_variance), 0.0, 1.0));
513
+ direction_view_to_world(-surface.incident, camera), clamp(sqrt(water.roughness * water.roughness + pixel.variance), 0.0, 1.0));
388
514
  if (trace.reflections != 0u && above) {
389
515
  let reflected = water_hzb_hit(Ray(ray_at(origin, reflection_direction, 0.02), reflection_direction, limit),
390
516
  trace.fallback_steps);
@@ -468,12 +594,15 @@ export const WATER_RESOURCES = resources;
468
594
  function water_pipeline(shader, targets, finalize) {
469
595
  const code = shader.source;
470
596
  const layout = shader.resources.generatePipelineLayout(GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT);
471
- // Tile reconstruction spends six storage slots. Lighting spends four in the fragment stage.
472
- // Keep their visibility precise so the Brick4 variant fits the guaranteed stage budget.
473
- const vertex_only = new Set(['water_tiles', 'water_tile_sums', 'tessellation', 'meshlet_metadata', 'meshlet_data', 'wave_state', 'wave_grid']);
597
+ // The lattice spends eight storage slots in the vertex stage, the guaranteed budget. Lighting spends four in the
598
+ // fragment stage. Keep their visibility precise so the Brick4 variant fits it there.
599
+ const vertex_only = new Set(['lod', 'lod_limits', 'water_lod_bodies', 'water_lod_candidates', 'water_lod_cells',
600
+ 'meshlet_metadata', 'meshlet_data', 'water_band_displacement', 'water_particle_offsets', 'water_particle_entries']);
601
+ // the fragment stage shades the bands the mesh could not carry
602
+ const both = new Set(['camera', 'scene_database', 'water_band_derivatives', 'water_band_sampler']);
474
603
  shader.resources.groups.forEach((group, gi) => group.resources.forEach((resource, ri) => {
475
604
  layout.bindGroupLayouts[gi].entries[ri].visibility = vertex_only.has(resource.name) ? GPUShaderStage.VERTEX
476
- : resource.name === 'camera' || resource.name === 'scene_database' ? GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT
605
+ : both.has(resource.name) ? GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT
477
606
  : GPUShaderStage.FRAGMENT;
478
607
  }));
479
608
  return RenderPipelineDescriptor.from({