@woosh/meep-engine 3.41.0 → 3.43.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.
- package/build/bundle-worker-image-decoder.js +1 -1
- package/build/bundle-worker-terrain.js +1 -1
- package/editor/selection/editor_pick.d.ts.map +1 -1
- package/editor/selection/editor_pick.js +2 -1
- package/editor/tools/TransformToolV2.d.ts.map +1 -1
- package/editor/tools/TransformToolV2.js +7 -1
- package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
- package/editor/tools/engine/ToolEngine.js +10 -0
- package/editor/tools/v2/TransformControls.d.ts.map +1 -1
- package/editor/tools/v2/TransformControls.js +7 -20
- package/editor/tools/v2/handle/gizmo_drag_plane.d.ts.map +1 -1
- package/editor/tools/v2/handle/gizmo_drag_plane.js +4 -0
- package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
- package/editor/view/ecs/EntityEditor.js +3 -1
- package/editor/view/node-graph/NodeGraphView.d.ts.map +1 -1
- package/editor/view/node-graph/NodeGraphView.js +28 -12
- package/package.json +91 -96
- package/samples/engine/first-scene/index.html +4 -1
- package/samples/engine/first-scene/main.js +14 -9
- package/src/core/binary/utf8/utf8_decode.js +3 -3
- package/src/core/bvh2/binary/2/bvh32_query_user_data_ray.d.ts.map +1 -1
- package/src/core/bvh2/binary/2/bvh32_query_user_data_ray.js +112 -113
- package/src/core/bvh2/bvh3/ebvh_nodes_sort_sah_local4.js +2 -2
- package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +14 -14
- package/src/core/collection/RingBuffer.d.ts.map +1 -1
- package/src/core/collection/RingBuffer.js +4 -0
- package/src/core/collection/array/array_contains_duplicates.d.ts.map +1 -1
- package/src/core/collection/array/array_contains_duplicates.js +2 -1
- package/src/core/collection/array/array_copy.d.ts.map +1 -1
- package/src/core/collection/array/array_copy.js +23 -0
- package/src/core/collection/array/typed/sparse_typed_array_hash.d.ts +2 -2
- package/src/core/collection/array/typed/sparse_typed_array_hash.d.ts.map +1 -1
- package/src/core/collection/array/typed/sparse_typed_array_hash.js +9 -8
- package/src/core/collection/array/typed/uint16_array_hash.d.ts +2 -2
- package/src/core/collection/array/typed/uint16_array_hash.js +4 -4
- package/src/core/collection/array/typed/uint8_array_hash.d.ts +2 -2
- package/src/core/collection/array/typed/uint8_array_hash.js +4 -4
- package/src/core/collection/array/weightedRandomFromArray.d.ts +1 -0
- package/src/core/collection/array/weightedRandomFromArray.d.ts.map +1 -1
- package/src/core/collection/array/weightedRandomFromArray.js +9 -1
- package/src/core/collection/heap/BinaryHeap.d.ts.map +1 -1
- package/src/core/collection/heap/BinaryHeap.js +4 -0
- package/src/core/collection/list/List.d.ts.map +1 -1
- package/src/core/collection/list/List.js +4 -2
- package/src/core/collection/set/ArraySet.js +1 -1
- package/src/core/collection/set/HashSet.d.ts +1 -1
- package/src/core/collection/set/HashSet.d.ts.map +1 -1
- package/src/core/collection/set/HashSet.js +4 -2
- package/src/core/debug/matchers/IsEqual.js +1 -1
- package/src/core/debug/matchers/IsIterableContaining.d.ts.map +1 -1
- package/src/core/debug/matchers/IsIterableContaining.js +61 -74
- package/src/core/events/signal/SignalBinding.js +101 -101
- package/src/core/function/extractFunctionBody.js +13 -13
- package/src/core/function/objectsEqual.d.ts.map +1 -1
- package/src/core/function/objectsEqual.js +15 -14
- package/src/core/geom/3d/frustum/frustum3_compute_closest_point.d.ts.map +1 -1
- package/src/core/geom/3d/frustum/frustum3_compute_closest_point.js +207 -172
- package/src/core/geom/3d/gjk/gjk_epa_penetration.d.ts.map +1 -1
- package/src/core/geom/3d/gjk/gjk_epa_penetration.js +5 -4
- package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.d.ts.map +1 -1
- package/src/core/geom/3d/line/line3_compute_segment_point_distance_eikonal.js +8 -2
- package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts +20 -0
- package/src/core/geom/3d/triangle/tri3_ray_intersection.d.ts.map +1 -1
- package/src/core/geom/3d/triangle/tri3_ray_intersection.js +182 -0
- package/src/core/math/noise/curl_noise_3dt.d.ts.map +1 -1
- package/src/core/math/noise/curl_noise_3dt.js +20 -10
- package/src/core/math/noise/sdnoise.d.ts +16 -5
- package/src/core/math/noise/sdnoise.d.ts.map +1 -1
- package/src/core/math/noise/sdnoise.js +43 -9
- package/src/core/math/spline/spline3_hermite_nearest_point.d.ts +16 -1
- package/src/core/math/spline/spline3_hermite_nearest_point.d.ts.map +1 -1
- package/src/core/math/spline/spline3_hermite_nearest_point.js +44 -3
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +6 -2
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts +9 -0
- package/src/engine/control/first-person/FirstPersonPlayerController.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts +26 -0
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.d.ts.map +1 -1
- package/src/engine/control/first-person/FirstPersonPlayerControllerConfig.js +2 -0
- package/src/engine/control/first-person/abilities/AbilitySet.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/AbilitySet.js +9 -0
- package/src/engine/control/first-person/abilities/LedgeGrab.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/LedgeGrab.js +6 -0
- package/src/engine/control/first-person/abilities/Slide.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/Slide.js +1 -0
- package/src/engine/control/first-person/abilities/WallRun.d.ts +4 -1
- package/src/engine/control/first-person/abilities/WallRun.d.ts.map +1 -1
- package/src/engine/control/first-person/abilities/WallRun.js +13 -1
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts +1 -1
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.d.ts.map +1 -1
- package/src/engine/control/first-person/mastery/SlideInitiationTimingEvaluator.js +6 -5
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts +3 -3
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.d.ts.map +1 -1
- package/src/engine/control/first-person/mastery/StrideTimingJumpEvaluator.js +9 -8
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.d.ts.map +1 -1
- package/src/engine/control/first-person/sensors/FirstPersonSensorsSystem.js +13 -4
- package/src/engine/ecs/EntityManager.d.ts.map +1 -1
- package/src/engine/ecs/EntityManager.js +18 -15
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescription.js +2 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescriptionTable.d.ts.map +1 -1
- package/src/engine/ecs/dynamic_actions/rules/DynamicRuleDescriptionTable.js +1 -0
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
- package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +3 -0
- package/src/engine/ecs/parent/EntityNode.js +3 -3
- package/src/engine/ecs/speaker/VoiceSystem.d.ts.map +1 -1
- package/src/engine/ecs/speaker/VoiceSystem.js +4 -0
- package/src/engine/ecs/storage/BinaryBufferDeSerializer.js +5 -5
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +3 -2
- package/src/engine/ecs/storage/binary/object/BinaryObjectSerializationAdapter2.js +1 -1
- package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.d.ts.map +1 -1
- package/src/engine/ecs/storage/binary/object-v2/BinaryObjectDeSerializer.js +8 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.d.ts.map +1 -1
- package/src/engine/ecs/storage/populateEngineSerializationRegistry.js +8 -0
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +5 -2
- package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +3 -0
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +1 -0
- package/src/engine/ecs/terrain/overlay/TerrainOverlay.js +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTile.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTile.js +13 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
- package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +1 -0
- package/src/engine/graphics/ecs/light/LightSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/light/LightSerializationAdapter.js +13 -1
- package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.d.ts +5 -0
- package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/light/LightSerializationUpgrader_0_1.js +27 -0
- package/src/engine/graphics/ecs/water/Water.d.ts +11 -0
- package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/Water.js +17 -0
- package/src/engine/graphics/ecs/water/WaterSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterSerializationAdapter.js +3 -1
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.d.ts +9 -0
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterSerializationUpgrader_4_5.js +24 -0
- package/src/engine/graphics/ecs/water/WaterWaves.d.ts +30 -9
- package/src/engine/graphics/ecs/water/WaterWaves.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterWaves.js +41 -9
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.d.ts.map +1 -1
- package/src/engine/graphics/ecs/water/WaterWavesSerializationAdapter.js +30 -26
- package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.d.ts +6 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.d.ts.map +1 -0
- package/src/engine/graphics/ecs/water/WaterWavesSerializationUpgrader_0_1.js +22 -0
- package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.d.ts.map +1 -1
- package/src/engine/graphics/geometry/buffered/query/bvh32_geometry_raycast.js +3 -5
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.d.ts +20 -0
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.d.ts.map +1 -1
- package/src/engine/graphics/texture/3d/scs3d_sample_linear3.js +36 -0
- package/src/engine/graphics3/WaterSystem.d.ts +12 -3
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem.js +78 -46
- package/src/engine/input/devices/PointerDevice.d.ts.map +1 -1
- package/src/engine/input/devices/PointerDevice.js +29 -4
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts +1 -0
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.d.ts.map +1 -1
- package/src/engine/intelligence/behavior/selector/WeightedRandomBehavior.js +14 -3
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_advect_maccormack_velocity.js +12 -3
- package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_advect_sl_velocity.js +8 -2
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts +19 -0
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_grid_sample_scalar_masked.js +41 -2
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_mac_advect_scalar.js +9 -3
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.d.ts.map +1 -1
- package/src/engine/physics/fluid/solver/v3_mac_advect_sl_velocity.js +26 -10
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +3 -6
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
- package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.js +22 -9
- package/src/engine/ui/DraggableAspect.d.ts +5 -1
- package/src/engine/ui/DraggableAspect.d.ts.map +1 -1
- package/src/engine/ui/DraggableAspect.js +61 -4
- package/src/format/image/avif/heif/ItemProperty.d.ts +1 -1
- package/src/format/image/avif/heif/ItemProperty.js +1 -1
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.d.ts.map +1 -1
- package/src/format/image/png/chunk/png_chunk_decode_iTXt.js +17 -6
- package/src/shade/device/mock/SoftwareGPUDevice.d.ts +2 -4
- package/src/shade/device/mock/SoftwareGPUDevice.d.ts.map +1 -1
- package/src/shade/device/mock/SoftwareGPUDevice.js +20 -13
- package/src/shade/playground/skinned_blas_refit/sweep_views.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/sweep_views.js +2 -1
- package/src/shade/playground/vgeo_runtime/README.md +5 -4
- package/src/shade/playground/water_refraction/README.md +10 -11
- package/src/shade/playground/water_refraction/main.js +9 -8
- package/src/shade/playground/water_refraction/world.js +1 -1
- package/src/shade/playground/water_surface/README.md +43 -0
- package/src/shade/playground/water_surface/index.html +30 -0
- package/src/shade/playground/water_surface/main.d.ts +2 -0
- package/src/shade/playground/water_surface/main.d.ts.map +1 -0
- package/src/shade/playground/water_surface/main.js +154 -0
- package/src/shade/playground/water_surface/world.d.ts +44 -0
- package/src/shade/playground/water_surface/world.d.ts.map +1 -0
- package/src/shade/playground/water_surface/world.js +160 -0
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +6 -5
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +5 -0
- package/src/shade/renderer/atmosphere/GPUSky.d.ts +12 -0
- package/src/shade/renderer/atmosphere/GPUSky.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/GPUSky.js +162 -147
- package/src/shade/renderer/binding/BufferBinding.js +2 -2
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +5 -3
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts +10 -3
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.js +17 -9
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +3 -0
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +16 -0
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +2 -0
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +1 -1
- package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +11 -0
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +1 -1
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +9 -3
- package/src/shade/renderer/geometry/util/geometry_simplify_redundant_topology.d.ts.map +1 -1
- package/src/shade/renderer/geometry/util/geometry_simplify_redundant_topology.js +2 -2
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +85 -0
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +12 -6
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +7 -0
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -1
- package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +3 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_denoise_sh.d.ts.map +1 -1
- package/src/shade/renderer/lightmap/bake/shader_lightmap_denoise_sh.js +10 -3
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/particles/DESIGN.md +13 -9
- package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts +3 -2
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/native/avboit/AVBOITSideChannel.js +3 -2
- package/src/shade/renderer/scene/GPUSceneContext.d.ts +9 -1
- package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
- package/src/shade/renderer/scene/GPUSceneContext.js +23 -1
- package/src/shade/renderer/scene/SCENE_DATABASE_SPEC.js +2 -1
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.d.ts +13 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128_inverse.js +42 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.d.ts +15 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.d.ts.map +1 -0
- package/src/shade/renderer/shader/chunk/math/fft/chunk_fft128x4_inverse_workgroup.js +39 -0
- package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +2 -0
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +4 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +74 -3
- package/src/shade/renderer/volumetrics/NOTES.md +1023 -957
- package/src/shade/renderer/volumetrics/chunk_volumetrics_irradiance_sh.d.ts +5 -4
- package/src/shade/renderer/volumetrics/chunk_volumetrics_irradiance_sh.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/chunk_volumetrics_irradiance_sh.js +5 -4
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.d.ts +1 -1
- package/src/shade/renderer/volumetrics/gi/chunk_volumetrics_gi.js +1 -1
- package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.d.ts +1 -1
- package/src/shade/renderer/volumetrics/gi/graph_build_froxel_gi.js +1 -1
- package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts +41 -0
- package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/graph_build_froxel_irradiance.js +112 -0
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +9 -23
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +18 -126
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts +22 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.d.ts.map +1 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_irradiance.js +228 -0
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +0 -53
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts +15 -0
- package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
- package/src/shade/renderer/water/GPUWaterRenderer.js +110 -30
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +16 -8
- package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +1 -1
- package/src/shade/renderer/water/medium/GPUWaterBodies.js +31 -26
- package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts +0 -1
- package/src/shade/renderer/water/medium/shader_water_meshlets.d.ts.map +1 -1
- package/src/shade/renderer/water/medium/shader_water_meshlets.js +1 -39
- package/src/shade/renderer/water/shader_water.d.ts +0 -1
- package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
- package/src/shade/renderer/water/shader_water.js +201 -72
- package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts +70 -0
- package/src/shade/renderer/water/surface/GPUWaterSurface.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/GPUWaterSurface.js +199 -0
- package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts +5 -2
- package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/WATER_WAVE_RECORD_STRUCT.js +32 -12
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts +23 -0
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/chunk_water_surface_sample.js +227 -0
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +65 -11
- package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts.map +1 -1
- package/src/shade/renderer/water/surface/graph_water_tessellation.js +163 -28
- package/src/shade/renderer/water/surface/lod/WATER_LOD.d.ts +51 -0
- package/src/shade/renderer/water/surface/lod/WATER_LOD.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/WATER_LOD.js +95 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod.d.ts +10 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod.js +127 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.d.ts +17 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/chunk_water_lod_cell.js +246 -0
- package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts +118 -0
- package/src/shade/renderer/water/surface/lod/shader_water_lod.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/lod/shader_water_lod.js +727 -0
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts +60 -0
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/GPUWaterParticles.js +274 -0
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts +17 -0
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/WATER_PARTICLE_STRUCT.js +57 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts +43 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle.js +268 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts +20 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.js +239 -0
- package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts +153 -0
- package/src/shade/renderer/water/surface/particles/shader_water_particles.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/particles/shader_water_particles.js +874 -0
- package/src/shade/renderer/water/surface/shader_water_cull.d.ts +42 -0
- package/src/shade/renderer/water/surface/shader_water_cull.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_cull.js +269 -0
- package/src/shade/renderer/water/surface/shader_water_waves_combine.d.ts +15 -0
- package/src/shade/renderer/water/surface/shader_water_waves_combine.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_waves_combine.js +73 -0
- package/src/shade/renderer/water/surface/shader_water_waves_derive.d.ts +11 -0
- package/src/shade/renderer/water/surface/shader_water_waves_derive.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/shader_water_waves_derive.js +78 -0
- package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.d.ts +35 -0
- package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/GPUWaterSpectrum.js +103 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.d.ts +22 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_BAND_STATE.js +57 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.d.ts +22 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/WATER_SPECTRUM.js +25 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.d.ts +14 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_jonswap.js +48 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.d.ts +23 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/chunk_water_spectrum.js +169 -0
- package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.d.ts +26 -0
- package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.d.ts.map +1 -0
- package/src/shade/renderer/water/surface/spectrum/shader_water_spectrum.js +161 -0
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +29 -9
- package/src/shade/wgsl/emulator/WGSLLanguageCore.d.ts.map +1 -1
- package/src/shade/wgsl/emulator/WGSLLanguageCore.js +14 -3
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts +0 -28
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.d.ts.map +0 -1
- package/src/core/geom/2d/polygon/polygon2_triangulate.corpus.js +0 -86
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.d.ts +0 -29
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.d.ts.map +0 -1
- package/src/engine/ecs/storage/binary/object-v2/serializerTestHarness.js +0 -44
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts +0 -4
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.d.ts.map +0 -1
- package/src/shade/renderer/water/medium/shader_water_bodies_derive.js +0 -32
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts +0 -25
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/GPUWaterWaveSimulation.js +0 -128
- package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts +0 -9
- package/src/shade/renderer/water/surface/chunk_water_tessellation.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/chunk_water_tessellation.js +0 -144
- package/src/shade/renderer/water/surface/chunk_water_wave.d.ts +0 -13
- package/src/shade/renderer/water/surface/chunk_water_wave.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/chunk_water_wave.js +0 -40
- package/src/shade/renderer/water/surface/shader_water_tiles.d.ts +0 -4
- package/src/shade/renderer/water/surface/shader_water_tiles.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/shader_water_tiles.js +0 -123
- package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts +0 -6
- package/src/shade/renderer/water/surface/shader_water_wave_batch.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/shader_water_wave_batch.js +0 -85
- package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts +0 -6
- package/src/shade/renderer/water/surface/shader_water_wave_domain.d.ts.map +0 -1
- package/src/shade/renderer/water/surface/shader_water_wave_domain.js +0 -83
|
@@ -0,0 +1,727 @@
|
|
|
1
|
+
import { CAMERA_UNIFORM_STRUCT } from "../../../camera/CAMERA_UNIFORM_STRUCT.js";
|
|
2
|
+
import { GEOMETRY_METADATA_STRUCT } from "../../../geometry/GEOMETRY_METADATA_STRUCT.js";
|
|
3
|
+
import { GEOMETRY_MESHLET_TRIANGLE_LIMIT } from "../../../geometry/meshlet/GEOMETRY_MESHLET_TRIANGLE_LIMIT.js";
|
|
4
|
+
import { MESHLET_METADATA_STRUCT } from "../../../geometry/meshlet/encoding/MESHLET_METADATA_STRUCT.js";
|
|
5
|
+
import { COLLECTION_U32_STRUCT_READ } from "../../../gpu_primitive/COLLECTION_U32_STRUCT_READ.js";
|
|
6
|
+
import {
|
|
7
|
+
WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION
|
|
8
|
+
} from "../../../gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.js";
|
|
9
|
+
import { MESHLET_COLLECTION_STRUCT } from "../../../rasterize/MESHLET_COLLECTION_STRUCT.js";
|
|
10
|
+
import { chunk_scene_database_access } from "../../../scene/chunk_scene_database_access.js";
|
|
11
|
+
import { ComputeShader } from "../../../shader/ComputeShader.js";
|
|
12
|
+
import { CodeChunk } from "../../../shader/compiler/CodeChunk.js";
|
|
13
|
+
import { chunk_aabb3_intersects_frustum } from "../../../shader/chunk/geometry/aabb3/chunk_aabb3_intersects_frustum.js";
|
|
14
|
+
import { chunk_aabb3_project } from "../../../shader/chunk/geometry/aabb3/chunk_aabb3_project.js";
|
|
15
|
+
import { chunk_array_to_aabb3 } from "../../../shader/chunk/geometry/aabb3/chunk_array_to_aabb3.js";
|
|
16
|
+
import { chunk_mat3_inverse } from "../../../shader/chunk/geometry/mat3/chunk_mat3_inverse.js";
|
|
17
|
+
import { chunk_mat4_linear_determinant } from "../../../shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js";
|
|
18
|
+
import { chunk_read_meshlet_vertex_by_draw_index } from "../../../shader/chunk/geometry/meshlet/chunk_read_meshlet_vertex_by_draw_index.js";
|
|
19
|
+
import { chunk_folded_workgroup_index } from "../../../shader/chunk/grid/chunk_folded_workgroup_index.js";
|
|
20
|
+
import { chunk_lower_bound_branchless } from "../../../shader/chunk/math/chunk_lower_bound_branchless.js";
|
|
21
|
+
import { ShaderResourceSetDescriptor } from "../../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
22
|
+
import { atomic_u32, f32, u32, vec2i } from "../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
23
|
+
import { WebGPUArray } from "../../../shader/type/WebGPUArray.js";
|
|
24
|
+
import { WebGPUStruct } from "../../../shader/type/WebGPUStruct.js";
|
|
25
|
+
import { WATER_BODIES_TABLE, WATER_WAVES_TABLE } from "../../medium/chunk_water_bodies.js";
|
|
26
|
+
import { chunk_water_particle } from "../particles/chunk_water_particle.js";
|
|
27
|
+
import { chunk_water_spectrum } from "../spectrum/chunk_water_spectrum.js";
|
|
28
|
+
import { chunk_water_lod_cell } from "./chunk_water_lod_cell.js";
|
|
29
|
+
import { WATER_LOD_BODY_STRUCT, WATER_LOD_VIEW_STRUCT, WaterLodMode } from "./WATER_LOD.js";
|
|
30
|
+
|
|
31
|
+
/** Vertices a cell of each list draws: an interior cell's quad, a boundary cell's clipped polygon fan, a triangle. */
|
|
32
|
+
export const WATER_LOD_INTERIOR_VERTICES = 6;
|
|
33
|
+
export const WATER_LOD_BOUNDARY_VERTICES = 15;
|
|
34
|
+
export const WATER_LOD_TRIANGLE_VERTICES = 3;
|
|
35
|
+
/** First vertex of each list's draw: the vertex stage tells the lists apart by it. */
|
|
36
|
+
export const WATER_LOD_BOUNDARY_FIRST_VERTEX = 16;
|
|
37
|
+
export const WATER_LOD_TRIANGLE_FIRST_VERTEX = 32;
|
|
38
|
+
/** A patch's base vertex: its indices name its 9 × 9 lattice points from here. */
|
|
39
|
+
export const WATER_LOD_PATCH_FIRST_VERTEX = 64;
|
|
40
|
+
/** Lattice points along a patch's side: a quadrant's 8 cells and their far edge. */
|
|
41
|
+
export const WATER_LOD_PATCH_SIDE = 9;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A quadrant inside its triangle drawn as one indexed patch: its 64 cells as two triangles each, row by row, over its
|
|
45
|
+
* 9 × 9 lattice points, so the vertex stage runs about once a point rather than six times a cell. Each cell turns as
|
|
46
|
+
* an interior cell's quad does, its diagonal from its first corner to its third.
|
|
47
|
+
*/
|
|
48
|
+
export const WATER_LOD_PATCH_INDICES = (() => {
|
|
49
|
+
const side = WATER_LOD_PATCH_SIDE, out = new Uint16Array((side - 1) * (side - 1) * 6);
|
|
50
|
+
let at = 0;
|
|
51
|
+
for (let z = 0; z + 1 < side; z++) for (let x = 0; x + 1 < side; x++) {
|
|
52
|
+
const a = z * side + x, b = a + 1, c = a + side + 1, d = a + side;
|
|
53
|
+
out.set([a, b, c, a, c, d], at);
|
|
54
|
+
at += 6;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
})();
|
|
58
|
+
|
|
59
|
+
/** Bits of a cell record that hold the cell's place in its candidate's node: quadrant · 64 + row · 8 + column. */
|
|
60
|
+
export const WATER_LOD_CELL_LOCAL_BITS = 8;
|
|
61
|
+
/** Most candidates a frame can hold: the rest of a cell record's bits. */
|
|
62
|
+
export const WATER_LOD_CANDIDATE_LIMIT = 2 ** (32 - WATER_LOD_CELL_LOCAL_BITS);
|
|
63
|
+
|
|
64
|
+
/** Words a boundary cell's clipped polygon is kept in, beside its record: see water_lod_polygon_pack. */
|
|
65
|
+
export const WATER_LOD_POLYGON_WORDS = 2;
|
|
66
|
+
|
|
67
|
+
/** Cell lists, in the order their scans, budgets and draws are kept. */
|
|
68
|
+
export const WaterLodList = Object.freeze({ Interior: 0, Boundary: 1, Triangle: 2, Patch: 3 });
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* One (authored triangle, lattice node) pair the frame considered.
|
|
72
|
+
* `packed`: bits 0–6 the triangle in its meshlet, bits 8–12 the level,
|
|
73
|
+
* 16–19 the quadrants drawn, 20–23 those of them inside the triangle.
|
|
74
|
+
*/
|
|
75
|
+
export const WATER_LOD_CANDIDATE_STRUCT = WebGPUStruct.from({
|
|
76
|
+
meshlet: u32, body: u32, packed: u32, reserved: u32, node: vec2i,
|
|
77
|
+
}, 'WaterLodCandidate');
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Budgets a frame's lists are cut to, and so where each list's records start in the cell buffer: interior cells,
|
|
81
|
+
* boundary cells, triangles drawn as themselves (two words each), patches, then the boundary cells' polygons
|
|
82
|
+
* ({@link WATER_LOD_POLYGON_WORDS} each).
|
|
83
|
+
*/
|
|
84
|
+
export const WATER_LOD_LIMITS_STRUCT = WebGPUStruct.from({
|
|
85
|
+
candidates: u32, interior: u32, boundary: u32, triangles: u32, patches: u32,
|
|
86
|
+
}, 'WaterLodLimits').pack();
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Words of the per-view statistics, totals before budgets: candidates, interior cells, boundary cells, flat triangles,
|
|
90
|
+
* patches.
|
|
91
|
+
*/
|
|
92
|
+
export const WATER_LOD_STATISTICS_WORDS = 5;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The cell lists' counts, kept as one collection and scanned once: for the frame's n candidates, the interior counts,
|
|
96
|
+
* then the boundary counts, then the patch counts. A list's sums are its region of the scan less what came before it.
|
|
97
|
+
*/
|
|
98
|
+
const chunk_water_lod_list_sums = CodeChunk.from(/* language=WGSL */ `
|
|
99
|
+
// A list's region of the counts: interior 0, boundary 1, patches 2.
|
|
100
|
+
fn water_lod_list_region(list: u32) -> u32 { return select(list, 2u, list == ${WaterLodList.Patch}u); }
|
|
101
|
+
|
|
102
|
+
// The sum before a region's first element, and the region's total.
|
|
103
|
+
fn water_lod_list_span(sums: ptr<storage, array<u32>, read>, count: u32, region: u32) -> vec2<u32> {
|
|
104
|
+
let n = count / 3u;
|
|
105
|
+
if (n == 0u) { return vec2<u32>(0u); }
|
|
106
|
+
let before = select(0u, sums[region * n - 1u], region > 0u);
|
|
107
|
+
return vec2<u32>(before, sums[(region + 1u) * n - 1u] - before);
|
|
108
|
+
}
|
|
109
|
+
`);
|
|
110
|
+
|
|
111
|
+
const chunk_water_lod_layout = CodeChunk.from(/* language=WGSL */ `
|
|
112
|
+
fn water_lod_triangles_base(limits: ${WATER_LOD_LIMITS_STRUCT.wgsl_ref}) -> u32 { return limits.interior + limits.boundary; }
|
|
113
|
+
fn water_lod_patches_base(limits: ${WATER_LOD_LIMITS_STRUCT.wgsl_ref}) -> u32 { return water_lod_triangles_base(limits) + 2u * limits.triangles; }
|
|
114
|
+
fn water_lod_polygons_base(limits: ${WATER_LOD_LIMITS_STRUCT.wgsl_ref}) -> u32 { return water_lod_patches_base(limits) + limits.patches; }
|
|
115
|
+
`, [WATER_LOD_LIMITS_STRUCT.declaration_chunk]);
|
|
116
|
+
export { chunk_water_lod_layout };
|
|
117
|
+
|
|
118
|
+
const chunk_water_lod_candidate = CodeChunk.from(/* language=WGSL */ `
|
|
119
|
+
fn water_lod_candidate_triangle(packed: u32) -> u32 { return packed & 127u; }
|
|
120
|
+
fn water_lod_candidate_level(packed: u32) -> u32 { return (packed >> 8u) & 31u; }
|
|
121
|
+
fn water_lod_candidate_drawn(packed: u32) -> u32 { return (packed >> 16u) & 15u; }
|
|
122
|
+
fn water_lod_candidate_interior(packed: u32) -> u32 { return (packed >> 20u) & 15u; }
|
|
123
|
+
|
|
124
|
+
// The square of nodes of a level that overlap the triangle's bounds and reach into the level's range. (xy: first
|
|
125
|
+
// node, zw: nodes along x and z.) The two are intersected as node ranges, not as boxes: a node can overlap the
|
|
126
|
+
// bounds at one side and come within range at the other while the bounds themselves stop short of the range. The
|
|
127
|
+
// reach is taken a little long — a thousandth of a node and a few rounding steps of the camera's place — so that a
|
|
128
|
+
// node whose edge lies at it, which water_lod_node_exists keeps, is never cut here; every candidate passes through
|
|
129
|
+
// that test, which decides.
|
|
130
|
+
fn water_lod_node_rect(view: ${WATER_LOD_VIEW_STRUCT.wgsl_ref}, body: ${WATER_LOD_BODY_STRUCT.wgsl_ref}, level: u32,
|
|
131
|
+
lo: vec2<f32>, hi: vec2<f32>) -> vec4<i32> {
|
|
132
|
+
let size = water_lod_cell(level) * f32(WATER_LOD_NODE_CELLS);
|
|
133
|
+
var first = vec2<i32>(floor(lo / size));
|
|
134
|
+
var last = vec2<i32>(floor(hi / size));
|
|
135
|
+
if (view.orthographic == 0u && level < WATER_LOD_COARSEST) {
|
|
136
|
+
let range = water_lod_range(view, level);
|
|
137
|
+
if (range < body.vertical) { return vec4<i32>(0); }
|
|
138
|
+
let slack = 1e-3 * size + 2.4e-7 * max(abs(body.camera.x), abs(body.camera.z));
|
|
139
|
+
let reach = sqrt(range * range - body.vertical * body.vertical) + slack;
|
|
140
|
+
first = max(first, vec2<i32>(floor((body.camera.xz - vec2<f32>(reach)) / size)));
|
|
141
|
+
last = min(last, vec2<i32>(floor((body.camera.xz + vec2<f32>(reach)) / size)));
|
|
142
|
+
}
|
|
143
|
+
return vec4<i32>(first, max(last - first + vec2<i32>(1), vec2<i32>(0)));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The triangle's x-interval on the line of constant z in metric xz; (1e30, -1e30) when it misses it.
|
|
147
|
+
fn water_lod_line(tri: WaterLodTriangle, z: f32) -> vec2<f32> {
|
|
148
|
+
var span = vec2<f32>(1e30, -1e30);
|
|
149
|
+
for (var e = 0u; e < 3u; e++) {
|
|
150
|
+
let p = tri.p[e];
|
|
151
|
+
let q = tri.p[(e + 1u) % 3u];
|
|
152
|
+
if ((p.y - z) * (q.y - z) > 0.0) { continue; }
|
|
153
|
+
if (p.y == q.y) { span = vec2<f32>(min(span.x, min(p.x, q.x)), max(span.y, max(p.x, q.x))); continue; }
|
|
154
|
+
let x = mix(p.x, q.x, (z - p.y) / (q.y - p.y));
|
|
155
|
+
span = vec2<f32>(min(span.x, x), max(span.y, x));
|
|
156
|
+
}
|
|
157
|
+
return span;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// One row of a quadrant cut by a triangle, in absolute lattice columns: the columns whose morphed cells can meet the
|
|
161
|
+
// triangle (x … y), and of those the run lying wholly inside it (z … w, empty as w = z − 1). A morph slides a cell's odd
|
|
162
|
+
// corners down by up to morph cells, the most any point of the quadrant takes, so each cell is taken as its own
|
|
163
|
+
// square grown that far down in x and z: its row as the strip from morph below it to its top. A cell meets the
|
|
164
|
+
// triangle when such a box overlaps the triangle's span over the strip; the triangle is convex, so the box is inside it
|
|
165
|
+
// exactly when its span lies inside the triangle's span on both of the strip's edge lines, taken a thousandth of a
|
|
166
|
+
// cell inwards so that rounding can only send a cell to the clipper, never past it. Where nothing morphs the boxes are
|
|
167
|
+
// the cells themselves, and only cells an authored edge cuts go to the clipper.
|
|
168
|
+
fn water_lod_row_runs(tri: WaterLodTriangle, level: u32, node: vec2<i32>, quadrant: u32, row: u32, morph: f32) -> vec4<i32> {
|
|
169
|
+
let origin = water_lod_quadrant_origin(node, quadrant);
|
|
170
|
+
let cell = water_lod_cell(level);
|
|
171
|
+
let z = origin.y + i32(row);
|
|
172
|
+
let za = (f32(z) - morph) * cell;
|
|
173
|
+
let zb = f32(z + 1) * cell;
|
|
174
|
+
let bottom = water_lod_line(tri, za);
|
|
175
|
+
let top = water_lod_line(tri, zb);
|
|
176
|
+
var span = vec2<f32>(min(bottom.x, top.x), max(bottom.y, top.y));
|
|
177
|
+
for (var k = 0u; k < 3u; k++) {
|
|
178
|
+
let p = tri.p[k];
|
|
179
|
+
if (p.y >= za && p.y <= zb) { span = vec2<f32>(min(span.x, p.x), max(span.y, p.x)); }
|
|
180
|
+
}
|
|
181
|
+
if (span.x > span.y) { return vec4<i32>(origin.x, origin.x - 1, origin.x, origin.x - 1); }
|
|
182
|
+
let a = max(origin.x, i32(floor(span.x / cell)));
|
|
183
|
+
let b = min(origin.x + WATER_LOD_QUADRANT_CELLS - 1, i32(ceil(span.y / cell + morph)) - 1);
|
|
184
|
+
if (a > b) { return vec4<i32>(a, a - 1, a, a - 1); }
|
|
185
|
+
let margin = 1e-3 * cell;
|
|
186
|
+
let inner = vec2<f32>(max(bottom.x, top.x) + margin, min(bottom.y, top.y) - margin);
|
|
187
|
+
let c = max(a, i32(ceil(inner.x / cell + morph)));
|
|
188
|
+
let d = min(b, i32(floor(inner.y / cell)) - 1);
|
|
189
|
+
if (c > d) { return vec4<i32>(a, b, b + 1, b); }
|
|
190
|
+
return vec4<i32>(a, b, c, d);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// A list's cells of a quadrant cut by a triangle, by row: (interior, boundary) counts of one row.
|
|
194
|
+
fn water_lod_row_counts(runs: vec4<i32>) -> vec2<u32> {
|
|
195
|
+
let interior = max(runs.w - runs.z + 1, 0);
|
|
196
|
+
let overlap = max(runs.y - runs.x + 1, 0);
|
|
197
|
+
return vec2<u32>(u32(interior), u32(overlap - interior));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
fn water_lod_triangle_lo(tri: WaterLodTriangle) -> vec2<f32> { return min(min(tri.p[0], tri.p[1]), tri.p[2]); }
|
|
201
|
+
fn water_lod_triangle_hi(tri: WaterLodTriangle) -> vec2<f32> { return max(max(tri.p[0], tri.p[1]), tri.p[2]); }
|
|
202
|
+
|
|
203
|
+
fn water_lod_triangle_levels(view: ${WATER_LOD_VIEW_STRUCT.wgsl_ref}, body: ${WATER_LOD_BODY_STRUCT.wgsl_ref}, tri: WaterLodTriangle) -> vec2<u32> {
|
|
204
|
+
let lo = water_lod_triangle_lo(tri);
|
|
205
|
+
let hi = water_lod_triangle_hi(tri);
|
|
206
|
+
return water_lod_level_span(view, water_lod_nearest(view, body, lo, hi), water_lod_farthest(view, body, lo, hi));
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// A triangle too steep to cut from the horizontal lattice: more than 75° from the body's metric up.
|
|
210
|
+
fn water_lod_steep(a: vec3<f32>, b: vec3<f32>, c: vec3<f32>, scale: vec3<f32>) -> bool {
|
|
211
|
+
let face = cross((b - a) * scale, (c - a) * scale);
|
|
212
|
+
return abs(face.y) < 0.25881905 * length(face);
|
|
213
|
+
}
|
|
214
|
+
`, [chunk_water_lod_cell, WATER_LOD_VIEW_STRUCT.declaration_chunk, WATER_LOD_BODY_STRUCT.declaration_chunk]);
|
|
215
|
+
|
|
216
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
217
|
+
|
|
218
|
+
const bodies_resources = new ShaderResourceSetDescriptor();
|
|
219
|
+
bodies_resources.setImmediate('capacity', u32);
|
|
220
|
+
bodies_resources.createGroup()
|
|
221
|
+
.addUniform('lod', WATER_LOD_VIEW_STRUCT)
|
|
222
|
+
.addUniform('camera', CAMERA_UNIFORM_STRUCT)
|
|
223
|
+
.addStorageBuffer('scene_database', WebGPUArray.u32)
|
|
224
|
+
.addStorageBuffer('geometries', WebGPUArray.from(GEOMETRY_METADATA_STRUCT))
|
|
225
|
+
.addStorageBuffer('visible_rows', COLLECTION_U32_STRUCT_READ)
|
|
226
|
+
.addStorageBuffer('steep', WebGPUArray.u32, true)
|
|
227
|
+
.addStorageBuffer('lod_bodies', WebGPUArray.from(WATER_LOD_BODY_STRUCT), true)
|
|
228
|
+
.addStorageBuffer('counts', COLLECTION_U32_STRUCT_READ, true);
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* The view's draw culling (WATER_SURFACE_DISTANCE_LOD_PROPOSAL_2026_10_06.md §4.3), one thread a body of the culling
|
|
232
|
+
* pass's visible list: against the view with the body's exact bounds, and for a body drawn, how this view sees it —
|
|
233
|
+
* the camera in its metric local frame and whether it is cut from the lattice or drawn as its authored triangles —
|
|
234
|
+
* and its meshlet count at its row, for an ordered expansion. A drawn body's steep flag starts again: its triangles
|
|
235
|
+
* set it.
|
|
236
|
+
*/
|
|
237
|
+
export const shader_water_lod_bodies = ComputeShader.from({ label: 'Water / lod bodies', resources: bodies_resources,
|
|
238
|
+
body: CodeChunk.from(/* language=WGSL */ `
|
|
239
|
+
@compute @workgroup_size(64)
|
|
240
|
+
fn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_index) lane: u32) {
|
|
241
|
+
let index = folded_workgroup_index(group) * 64u + lane;
|
|
242
|
+
if (index == 0u) { counts.count = capacity; }
|
|
243
|
+
if (index >= visible_rows.count) { return; }
|
|
244
|
+
let row = visible_rows.elements[index];
|
|
245
|
+
let body = ${WATER_BODIES_TABLE.marshalling_method_read}(&scene_database, row);
|
|
246
|
+
var frame: ${WATER_LOD_BODY_STRUCT.wgsl_ref};
|
|
247
|
+
frame.mode = ${WaterLodMode.Hidden}u;
|
|
248
|
+
let global = scene_read_node(&scene_database, body.node).global;
|
|
249
|
+
if (abs(mat4_linear_determinant(global)) < 1e-20) { lod_bodies[row] = frame; return; }
|
|
250
|
+
let geometry = geometries[body.geometry];
|
|
251
|
+
let box = geometry.bounding_box;
|
|
252
|
+
let displacement = ${WATER_WAVES_TABLE.marshalling_method_read_field('displacement_bounds')}(&scene_database, body.wave);
|
|
253
|
+
var grown = box;
|
|
254
|
+
grown[1] += displacement.x; grown[4] += displacement.y;
|
|
255
|
+
grown[0] -= displacement.z; grown[2] -= displacement.z; grown[3] += displacement.z; grown[5] += displacement.z;
|
|
256
|
+
if (!aabb3_intersects_frustum(aabb3_project(array_to_aabb3(grown), global), camera.frustum)) { lod_bodies[row] = frame; return; }
|
|
257
|
+
|
|
258
|
+
let scale = max(vec3<f32>(length(global[0].xyz), length(global[1].xyz), length(global[2].xyz)), vec3<f32>(1e-20));
|
|
259
|
+
let linear = mat3x3<f32>(global[0].xyz, global[1].xyz, global[2].xyz);
|
|
260
|
+
let eye = camera.view_matrix_inverse[3].xyz;
|
|
261
|
+
let local = mat3_inverse(linear) * (eye - global[3].xyz);
|
|
262
|
+
frame.camera = local * scale;
|
|
263
|
+
frame.scale = scale;
|
|
264
|
+
let rest = vec2<f32>(box[1], box[4]) * scale.y;
|
|
265
|
+
frame.vertical = max(max(frame.camera.y - rest.y, rest.x - frame.camera.y), 0.0);
|
|
266
|
+
frame.bounds = vec2<f32>(displacement.y, displacement.z);
|
|
267
|
+
frame.rest = vec2<f32>(box[1], box[4]);
|
|
268
|
+
// cut from the lattice when something it displaces by can be drawn at its nearest: a band long enough, or a
|
|
269
|
+
// particle level whose packets can be
|
|
270
|
+
var lattice = false;
|
|
271
|
+
if (any(displacement.xyz != vec3<f32>(0.0))) {
|
|
272
|
+
let nearest = water_lod_nearest(lod, frame, vec2<f32>(box[0], box[2]) * scale.xz, vec2<f32>(box[3], box[5]) * scale.xz);
|
|
273
|
+
let finest = water_lod_footprint_min(lod, nearest);
|
|
274
|
+
let weights = ${WATER_WAVES_TABLE.marshalling_method_read_field('weights')}(&scene_database, body.wave);
|
|
275
|
+
for (var band = 0u; band < WATER_SPECTRUM_BANDS; band++) {
|
|
276
|
+
lattice = lattice || (weights[band] > 0.0 && water_band_wavelength(band) > 8.0 * finest);
|
|
277
|
+
}
|
|
278
|
+
let levels = ${WATER_WAVES_TABLE.marshalling_method_read_field('particle_levels')}(&scene_database, body.wave);
|
|
279
|
+
for (var level = 0u; level < WATER_PARTICLE_LEVELS; level++) {
|
|
280
|
+
lattice = lattice || ((levels & (1u << level)) != 0u && water_particle_cell_size(level) > 4.0 * finest);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
frame.mode = select(${WaterLodMode.Flat}u, ${WaterLodMode.Lattice}u, lattice);
|
|
284
|
+
lod_bodies[row] = frame;
|
|
285
|
+
steep[body.wave] = 0u;
|
|
286
|
+
counts.elements[row] = geometry.meshlets_count;
|
|
287
|
+
}
|
|
288
|
+
`, [WATER_BODIES_TABLE.chunk_read, WATER_WAVES_TABLE.chunk_read_field('displacement_bounds'),
|
|
289
|
+
WATER_WAVES_TABLE.chunk_read_field('weights'), WATER_WAVES_TABLE.chunk_read_field('particle_levels'),
|
|
290
|
+
chunk_water_lod_candidate, chunk_water_spectrum, chunk_water_particle, chunk_scene_database_access,
|
|
291
|
+
chunk_mat4_linear_determinant, chunk_mat3_inverse, chunk_aabb3_project, chunk_array_to_aabb3,
|
|
292
|
+
chunk_aabb3_intersects_frustum, chunk_folded_workgroup_index]) });
|
|
293
|
+
|
|
294
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
295
|
+
|
|
296
|
+
/** Reads a drawn triangle of the view's meshlet list: its body, its frame and its corners. */
|
|
297
|
+
const chunk_water_lod_source = CodeChunk.from(/* language=WGSL */ `
|
|
298
|
+
struct WaterLodSource {
|
|
299
|
+
meshlet: u32,
|
|
300
|
+
triangle: u32,
|
|
301
|
+
body_row: u32,
|
|
302
|
+
wave: u32,
|
|
303
|
+
node: u32,
|
|
304
|
+
frame: ${WATER_LOD_BODY_STRUCT.wgsl_ref},
|
|
305
|
+
a: vec3<f32>, b: vec3<f32>, c: vec3<f32>,
|
|
306
|
+
na: vec3<f32>, nb: vec3<f32>, nc: vec3<f32>,
|
|
307
|
+
present: bool,
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
fn water_lod_source(slot: u32) -> WaterLodSource {
|
|
311
|
+
var source: WaterLodSource;
|
|
312
|
+
let meshlet_slot = slot / ${GEOMETRY_MESHLET_TRIANGLE_LIMIT}u;
|
|
313
|
+
source.triangle = slot % ${GEOMETRY_MESHLET_TRIANGLE_LIMIT}u;
|
|
314
|
+
if (meshlet_slot >= water_meshlets.count) { return source; }
|
|
315
|
+
let definition = water_meshlets.elements[meshlet_slot];
|
|
316
|
+
let header = meshlet_metadata[definition.index];
|
|
317
|
+
source.meshlet = definition.index;
|
|
318
|
+
source.body_row = definition.mesh;
|
|
319
|
+
source.present = source.triangle < header.primitive_count;
|
|
320
|
+
if (!source.present) { return source; }
|
|
321
|
+
let body = ${WATER_BODIES_TABLE.marshalling_method_read}(&scene_database, definition.mesh);
|
|
322
|
+
source.wave = body.wave;
|
|
323
|
+
source.node = body.node;
|
|
324
|
+
source.frame = lod_bodies[definition.mesh];
|
|
325
|
+
let a = read_meshlet_vertex_by_draw_index(header, source.triangle * 3u);
|
|
326
|
+
let b = read_meshlet_vertex_by_draw_index(header, source.triangle * 3u + 1u);
|
|
327
|
+
let c = read_meshlet_vertex_by_draw_index(header, source.triangle * 3u + 2u);
|
|
328
|
+
source.a = a.position; source.b = b.position; source.c = c.position;
|
|
329
|
+
source.na = a.normal; source.nb = b.normal; source.nc = c.normal;
|
|
330
|
+
return source;
|
|
331
|
+
}
|
|
332
|
+
`, [WATER_LOD_BODY_STRUCT.declaration_chunk, WATER_BODIES_TABLE.chunk_read, chunk_read_meshlet_vertex_by_draw_index]);
|
|
333
|
+
|
|
334
|
+
const triangles_resources = new ShaderResourceSetDescriptor();
|
|
335
|
+
triangles_resources.createGroup()
|
|
336
|
+
.addUniform('lod', WATER_LOD_VIEW_STRUCT)
|
|
337
|
+
.addStorageBuffer('scene_database', WebGPUArray.u32)
|
|
338
|
+
.addStorageBuffer('water_meshlets', MESHLET_COLLECTION_STRUCT)
|
|
339
|
+
.addStorageBuffer('meshlet_metadata', WebGPUArray.from(MESHLET_METADATA_STRUCT))
|
|
340
|
+
.addStorageBuffer('meshlet_data', WebGPUArray.from(f32))
|
|
341
|
+
.addStorageBuffer('lod_bodies', WebGPUArray.from(WATER_LOD_BODY_STRUCT))
|
|
342
|
+
.addStorageBuffer('steep', WebGPUArray.from(atomic_u32), true)
|
|
343
|
+
.addStorageBuffer('counts', COLLECTION_U32_STRUCT_READ, true)
|
|
344
|
+
.addStorageBuffer('flats', COLLECTION_U32_STRUCT_READ, true);
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* One workgroup a drawn meshlet, one thread a triangle slot: whether the triangle is drawn as itself — its body is flat
|
|
348
|
+
* in this view, or it is too steep for the lattice, which also marks its body steep so the next frame keeps it calm —
|
|
349
|
+
* and otherwise how many candidates it makes: the nodes of each level of its distance range that its bounds overlap
|
|
350
|
+
* within reach of the level's range.
|
|
351
|
+
*/
|
|
352
|
+
export const shader_water_lod_triangles = ComputeShader.from({ label: 'Water / lod triangles', resources: triangles_resources,
|
|
353
|
+
body: CodeChunk.from(/* language=WGSL */ `
|
|
354
|
+
@compute @workgroup_size(${GEOMETRY_MESHLET_TRIANGLE_LIMIT})
|
|
355
|
+
fn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_index) lane: u32) {
|
|
356
|
+
let meshlet_slot = folded_workgroup_index(group);
|
|
357
|
+
if (meshlet_slot == 0u && lane == 0u) {
|
|
358
|
+
counts.count = water_meshlets.count * ${GEOMETRY_MESHLET_TRIANGLE_LIMIT}u;
|
|
359
|
+
flats.count = counts.count;
|
|
360
|
+
}
|
|
361
|
+
if (meshlet_slot >= water_meshlets.count) { return; }
|
|
362
|
+
let slot = meshlet_slot * ${GEOMETRY_MESHLET_TRIANGLE_LIMIT}u + lane;
|
|
363
|
+
let source = water_lod_source(slot);
|
|
364
|
+
var candidates = 0u;
|
|
365
|
+
var flat = 0u;
|
|
366
|
+
if (source.present) {
|
|
367
|
+
let steep_triangle = water_lod_steep(source.a, source.b, source.c, source.frame.scale);
|
|
368
|
+
if (steep_triangle) { atomicOr(&steep[source.wave], 1u); }
|
|
369
|
+
if (steep_triangle || source.frame.mode != ${WaterLodMode.Lattice}u) {
|
|
370
|
+
flat = 1u;
|
|
371
|
+
} else {
|
|
372
|
+
let tri = water_lod_triangle(source.a, source.b, source.c, source.na, source.nb, source.nc, source.frame.scale);
|
|
373
|
+
let lo = water_lod_triangle_lo(tri);
|
|
374
|
+
let hi = water_lod_triangle_hi(tri);
|
|
375
|
+
let levels = water_lod_triangle_levels(lod, source.frame, tri);
|
|
376
|
+
// a triangle with no area draws nothing
|
|
377
|
+
for (var level = levels.x; level <= levels.y && !water_lod_no_area(tri); level++) {
|
|
378
|
+
let rect = water_lod_node_rect(lod, source.frame, level, lo, hi);
|
|
379
|
+
candidates += u32(rect.z * rect.w);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
counts.elements[slot] = candidates;
|
|
384
|
+
flats.elements[slot] = flat;
|
|
385
|
+
}
|
|
386
|
+
`, [chunk_water_lod_source, chunk_water_lod_candidate, chunk_scene_database_access, chunk_folded_workgroup_index]) });
|
|
387
|
+
|
|
388
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
const flats_resources = new ShaderResourceSetDescriptor();
|
|
391
|
+
flats_resources.setImmediate('limits', WebGPUStruct.from({ base: u32, budget: u32 }, 'WaterLodFlatLimits'));
|
|
392
|
+
flats_resources.createGroup()
|
|
393
|
+
.addStorageBuffer('water_meshlets', MESHLET_COLLECTION_STRUCT)
|
|
394
|
+
.addStorageBuffer('flat_sums', COLLECTION_U32_STRUCT_READ)
|
|
395
|
+
.addStorageBuffer('cells', WebGPUArray.u32, true);
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* One thread a triangle drawn as itself, up to its list's budget: its record, two words at the list's base in the cell
|
|
399
|
+
* buffer — its meshlet, and its body row and triangle — in the triangles' order.
|
|
400
|
+
*/
|
|
401
|
+
export const shader_water_lod_flats = ComputeShader.from({ label: 'Water / lod flats', resources: flats_resources,
|
|
402
|
+
body: CodeChunk.from(/* language=WGSL */ `
|
|
403
|
+
@compute @workgroup_size(64)
|
|
404
|
+
fn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_index) lane: u32) {
|
|
405
|
+
let index = folded_workgroup_index(group) * 64u + lane;
|
|
406
|
+
let total = select(0u, flat_sums.elements[max(flat_sums.count, 1u) - 1u], flat_sums.count > 0u);
|
|
407
|
+
if (index >= min(total, limits.budget)) { return; }
|
|
408
|
+
let slot = lower_bound_branchless(&flat_sums.elements, index + 1u, flat_sums.count);
|
|
409
|
+
let definition = water_meshlets.elements[slot / ${GEOMETRY_MESHLET_TRIANGLE_LIMIT}u];
|
|
410
|
+
cells[limits.base + 2u * index] = definition.index;
|
|
411
|
+
cells[limits.base + 2u * index + 1u] = (definition.mesh << 7u) | (slot % ${GEOMETRY_MESHLET_TRIANGLE_LIMIT}u);
|
|
412
|
+
}
|
|
413
|
+
`, [chunk_lower_bound_branchless, chunk_folded_workgroup_index]) });
|
|
414
|
+
|
|
415
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
416
|
+
|
|
417
|
+
/** Words of a candidate's row record: 32 rows (4 quadrants × 8), 16 bits each. */
|
|
418
|
+
export const WATER_LOD_CANDIDATE_ROW_WORDS = 16;
|
|
419
|
+
|
|
420
|
+
const candidates_resources = new ShaderResourceSetDescriptor();
|
|
421
|
+
candidates_resources.createGroup()
|
|
422
|
+
.addUniform('lod', WATER_LOD_VIEW_STRUCT)
|
|
423
|
+
.addUniform('limits', WATER_LOD_LIMITS_STRUCT)
|
|
424
|
+
.addUniform('camera', CAMERA_UNIFORM_STRUCT)
|
|
425
|
+
.addStorageBuffer('scene_database', WebGPUArray.u32)
|
|
426
|
+
.addStorageBuffer('water_meshlets', MESHLET_COLLECTION_STRUCT)
|
|
427
|
+
.addStorageBuffer('meshlet_metadata', WebGPUArray.from(MESHLET_METADATA_STRUCT))
|
|
428
|
+
.addStorageBuffer('meshlet_data', WebGPUArray.from(f32))
|
|
429
|
+
.addStorageBuffer('lod_bodies', WebGPUArray.from(WATER_LOD_BODY_STRUCT))
|
|
430
|
+
.addStorageBuffer('triangle_sums', COLLECTION_U32_STRUCT_READ)
|
|
431
|
+
.addStorageBuffer('candidates', WebGPUArray.from(WATER_LOD_CANDIDATE_STRUCT), true)
|
|
432
|
+
.addStorageBuffer('candidate_rows', WebGPUArray.u32, true)
|
|
433
|
+
.addStorageBuffer('cell_counts', COLLECTION_U32_STRUCT_READ, true);
|
|
434
|
+
|
|
435
|
+
/**
|
|
436
|
+
* One thread a candidate, up to the frame's budget: which of its node's quadrants the selection rule draws at its
|
|
437
|
+
* level, that meet the view and the triangle, which of those lie inside the triangle, and so how many patches, interior
|
|
438
|
+
* cells and boundary cells it draws. A quadrant inside the triangle is a patch. One the triangle only partly covers is
|
|
439
|
+
* taken row by row — the cells that can meet the triangle, of which the run wholly inside it is interior — and each
|
|
440
|
+
* row's runs are kept for the cells pass, 16 bits a row: first column, then the counts of the left boundary run, the
|
|
441
|
+
* inside run and the right boundary run. Its three counts go to their regions of the one collection the lists share.
|
|
442
|
+
*/
|
|
443
|
+
export const shader_water_lod_candidates = ComputeShader.from({ label: 'Water / lod candidates', resources: candidates_resources,
|
|
444
|
+
body: CodeChunk.from(/* language=WGSL */ `
|
|
445
|
+
fn water_lod_row_word(runs: vec4<i32>, origin: i32) -> u32 {
|
|
446
|
+
let left = u32(max(runs.z - runs.x, 0));
|
|
447
|
+
let inside = u32(max(runs.w - runs.z + 1, 0));
|
|
448
|
+
let right = u32(max(runs.y - runs.w, 0));
|
|
449
|
+
return u32(clamp(runs.x - origin, 0, 7)) | (left << 3u) | (inside << 7u) | (right << 11u);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
@compute @workgroup_size(64)
|
|
453
|
+
fn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_index) lane: u32) {
|
|
454
|
+
let index = folded_workgroup_index(group) * 64u + lane;
|
|
455
|
+
let total = select(0u, triangle_sums.elements[max(triangle_sums.count, 1u) - 1u], triangle_sums.count > 0u);
|
|
456
|
+
let count = min(total, limits.candidates);
|
|
457
|
+
if (index == 0u) { cell_counts.count = 3u * count; }
|
|
458
|
+
if (index >= count) { return; }
|
|
459
|
+
let slot = lower_bound_branchless(&triangle_sums.elements, index + 1u, triangle_sums.count);
|
|
460
|
+
var local = index;
|
|
461
|
+
if (slot > 0u) { local = index - triangle_sums.elements[slot - 1u]; }
|
|
462
|
+
let source = water_lod_source(slot);
|
|
463
|
+
let frame = source.frame;
|
|
464
|
+
var candidate: ${WATER_LOD_CANDIDATE_STRUCT.wgsl_ref};
|
|
465
|
+
candidate.meshlet = source.meshlet;
|
|
466
|
+
candidate.body = source.body_row;
|
|
467
|
+
candidate.packed = source.triangle;
|
|
468
|
+
let tri = water_lod_triangle(source.a, source.b, source.c, source.na, source.nb, source.nc, frame.scale);
|
|
469
|
+
let lo = water_lod_triangle_lo(tri);
|
|
470
|
+
let hi = water_lod_triangle_hi(tri);
|
|
471
|
+
let levels = water_lod_triangle_levels(lod, frame, tri);
|
|
472
|
+
var level = levels.x;
|
|
473
|
+
var rect = vec4<i32>(0);
|
|
474
|
+
for (; level <= levels.y; level++) {
|
|
475
|
+
rect = water_lod_node_rect(lod, frame, level, lo, hi);
|
|
476
|
+
let area = u32(rect.z * rect.w);
|
|
477
|
+
if (local < area) { break; }
|
|
478
|
+
local -= area;
|
|
479
|
+
}
|
|
480
|
+
let node = rect.xy + vec2<i32>(i32(local % u32(max(rect.z, 1))), i32(local / u32(max(rect.z, 1))));
|
|
481
|
+
candidate.node = node;
|
|
482
|
+
candidate.packed |= level << 8u;
|
|
483
|
+
var interior = 0u;
|
|
484
|
+
var boundary = 0u;
|
|
485
|
+
var patches = 0u;
|
|
486
|
+
var words: array<u32, ${WATER_LOD_CANDIDATE_ROW_WORDS}>;
|
|
487
|
+
if (water_lod_node_exists(lod, frame, level, node)) {
|
|
488
|
+
let global = scene_read_node(&scene_database, source.node).global;
|
|
489
|
+
let cell = water_lod_cell(level);
|
|
490
|
+
for (var quadrant = 0u; quadrant < 4u; quadrant++) {
|
|
491
|
+
let origin = water_lod_quadrant_origin(node, quadrant);
|
|
492
|
+
let q_lo = vec2<f32>(origin) * cell;
|
|
493
|
+
let q_hi = vec2<f32>(origin + vec2<i32>(WATER_LOD_QUADRANT_CELLS)) * cell;
|
|
494
|
+
if (!water_lod_quadrant_drawn(lod, frame, level, node, quadrant) || !water_lod_triangle_meets_square(tri, q_lo, q_hi)) { continue; }
|
|
495
|
+
// the quadrant's box in local units, grown by the body's bounds
|
|
496
|
+
let box_lo = vec3<f32>(q_lo.x / frame.scale.x - frame.bounds.y, frame.rest.x - frame.bounds.x, q_lo.y / frame.scale.z - frame.bounds.y);
|
|
497
|
+
let box_hi = vec3<f32>(q_hi.x / frame.scale.x + frame.bounds.y, frame.rest.y + frame.bounds.x, q_hi.y / frame.scale.z + frame.bounds.y);
|
|
498
|
+
if (!aabb3_intersects_frustum(aabb3_project(array_to_aabb3(array<f32, 6>(box_lo.x, box_lo.y, box_lo.z, box_hi.x, box_hi.y, box_hi.z)), global),
|
|
499
|
+
camera.frustum)) { continue; }
|
|
500
|
+
candidate.packed |= 1u << (16u + quadrant);
|
|
501
|
+
if (water_lod_triangle_holds_square(tri, q_lo, q_hi)) {
|
|
502
|
+
candidate.packed |= 1u << (20u + quadrant);
|
|
503
|
+
patches++;
|
|
504
|
+
continue;
|
|
505
|
+
}
|
|
506
|
+
// the most any point of the quadrant slides: its farthest corner's
|
|
507
|
+
let morph = water_lod_morph(lod, level, water_lod_farthest(lod, frame, q_lo, q_hi));
|
|
508
|
+
for (var row = 0u; row < 8u; row++) {
|
|
509
|
+
let runs = water_lod_row_runs(tri, level, node, quadrant, row, morph);
|
|
510
|
+
let counts = water_lod_row_counts(runs);
|
|
511
|
+
interior += counts.x;
|
|
512
|
+
boundary += counts.y;
|
|
513
|
+
let at = quadrant * 8u + row;
|
|
514
|
+
words[at >> 1u] |= water_lod_row_word(runs, origin.x) << (16u * (at & 1u));
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
candidates[index] = candidate;
|
|
519
|
+
for (var word = 0u; word < ${WATER_LOD_CANDIDATE_ROW_WORDS}u; word++) {
|
|
520
|
+
candidate_rows[index * ${WATER_LOD_CANDIDATE_ROW_WORDS}u + word] = words[word];
|
|
521
|
+
}
|
|
522
|
+
cell_counts.elements[index] = interior;
|
|
523
|
+
cell_counts.elements[count + index] = boundary;
|
|
524
|
+
cell_counts.elements[2u * count + index] = patches;
|
|
525
|
+
}
|
|
526
|
+
`, [chunk_water_lod_source, chunk_water_lod_candidate, WATER_LOD_CANDIDATE_STRUCT.declaration_chunk, chunk_scene_database_access,
|
|
527
|
+
chunk_lower_bound_branchless, chunk_aabb3_project, chunk_array_to_aabb3, chunk_aabb3_intersects_frustum,
|
|
528
|
+
chunk_folded_workgroup_index]) });
|
|
529
|
+
|
|
530
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
531
|
+
|
|
532
|
+
const cells_resources = new ShaderResourceSetDescriptor();
|
|
533
|
+
cells_resources.setImmediate('limits', WebGPUStruct.from({ base: u32, budget: u32, list: u32 }, 'WaterLodCellLimits'));
|
|
534
|
+
cells_resources.createGroup()
|
|
535
|
+
.addUniform('lod', WATER_LOD_VIEW_STRUCT)
|
|
536
|
+
.addUniform('lod_limits', WATER_LOD_LIMITS_STRUCT)
|
|
537
|
+
.addStorageBuffer('meshlet_metadata', WebGPUArray.from(MESHLET_METADATA_STRUCT))
|
|
538
|
+
.addStorageBuffer('meshlet_data', WebGPUArray.from(f32))
|
|
539
|
+
.addStorageBuffer('lod_bodies', WebGPUArray.from(WATER_LOD_BODY_STRUCT))
|
|
540
|
+
.addStorageBuffer('sums', COLLECTION_U32_STRUCT_READ)
|
|
541
|
+
.addStorageBuffer('candidates', WebGPUArray.from(WATER_LOD_CANDIDATE_STRUCT))
|
|
542
|
+
.addStorageBuffer('candidate_rows', WebGPUArray.u32)
|
|
543
|
+
.addStorageBuffer('cells', WebGPUArray.u32, true);
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* One thread a record of the interior, boundary or patch list, up to its budget: its candidate, and its place in the
|
|
547
|
+
* candidate's node (quadrant · 64 + row · 8 + column; a patch's first cell), at the list's base in the cell buffer,
|
|
548
|
+
* from the candidate's quadrant masks and row record: a patch for each quadrant inside the triangle in turn, and of
|
|
549
|
+
* the others, row by row, the inside run or the left and right boundary runs, in the order the candidate pass counted
|
|
550
|
+
* them. A boundary cell is clipped here, once, and its polygon kept beside the lists for the vertex stage to rebuild
|
|
551
|
+
* its points from.
|
|
552
|
+
*/
|
|
553
|
+
export const shader_water_lod_cells = ComputeShader.from({ label: 'Water / lod cells', resources: cells_resources,
|
|
554
|
+
body: CodeChunk.from(/* language=WGSL */ `
|
|
555
|
+
@compute @workgroup_size(64)
|
|
556
|
+
fn main(@builtin(workgroup_id) group: vec3u, @builtin(local_invocation_index) lane: u32) {
|
|
557
|
+
let index = folded_workgroup_index(group) * 64u + lane;
|
|
558
|
+
let region = water_lod_list_region(limits.list);
|
|
559
|
+
let span = water_lod_list_span(&sums.elements, sums.count, region);
|
|
560
|
+
if (index >= min(span.y, limits.budget)) { return; }
|
|
561
|
+
// the scan rises through every region, so the first sum past this record's lies in the list's own
|
|
562
|
+
let position = lower_bound_branchless(&sums.elements, span.x + index + 1u, sums.count);
|
|
563
|
+
let candidate_index = position - region * (sums.count / 3u);
|
|
564
|
+
var remaining = span.x + index - select(0u, sums.elements[max(position, 1u) - 1u], position > 0u);
|
|
565
|
+
let candidate = candidates[candidate_index];
|
|
566
|
+
let packed = candidate.packed;
|
|
567
|
+
let interior = limits.list == ${WaterLodList.Interior}u;
|
|
568
|
+
var place = 256u;
|
|
569
|
+
if (limits.list == ${WaterLodList.Patch}u) {
|
|
570
|
+
for (var quadrant = 0u; quadrant < 4u; quadrant++) {
|
|
571
|
+
if ((water_lod_candidate_interior(packed) & (1u << quadrant)) == 0u) { continue; }
|
|
572
|
+
if (remaining == 0u) { place = quadrant * 64u; break; }
|
|
573
|
+
remaining--;
|
|
574
|
+
}
|
|
575
|
+
cells[limits.base + index] = (candidate_index << ${WATER_LOD_CELL_LOCAL_BITS}u) | min(place, 255u);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
578
|
+
for (var quadrant = 0u; quadrant < 4u && place == 256u; quadrant++) {
|
|
579
|
+
let bit = 1u << quadrant;
|
|
580
|
+
if ((water_lod_candidate_drawn(packed) & bit) == 0u || (water_lod_candidate_interior(packed) & bit) != 0u) { continue; }
|
|
581
|
+
for (var row = 0u; row < 8u; row++) {
|
|
582
|
+
let at = quadrant * 8u + row;
|
|
583
|
+
let word = (candidate_rows[candidate_index * ${WATER_LOD_CANDIDATE_ROW_WORDS}u + (at >> 1u)] >> (16u * (at & 1u))) & 0xffffu;
|
|
584
|
+
let first = word & 7u;
|
|
585
|
+
let left = (word >> 3u) & 15u;
|
|
586
|
+
let inside = (word >> 7u) & 15u;
|
|
587
|
+
let right = (word >> 11u) & 15u;
|
|
588
|
+
if (interior) {
|
|
589
|
+
if (remaining < inside) { place = at * 8u + first + left + remaining; break; }
|
|
590
|
+
remaining -= inside;
|
|
591
|
+
} else {
|
|
592
|
+
if (remaining < left) { place = at * 8u + first + remaining; break; }
|
|
593
|
+
remaining -= left;
|
|
594
|
+
if (remaining < right) { place = at * 8u + first + left + inside + remaining; break; }
|
|
595
|
+
remaining -= right;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
cells[limits.base + index] = (candidate_index << ${WATER_LOD_CELL_LOCAL_BITS}u) | min(place, 255u);
|
|
600
|
+
if (interior || place >= 256u) { return; }
|
|
601
|
+
// the boundary cell clipped once here, rather than by every vertex of its fan in every pass that draws it
|
|
602
|
+
let frame = lod_bodies[candidate.body];
|
|
603
|
+
let header = meshlet_metadata[candidate.meshlet];
|
|
604
|
+
let triangle = water_lod_candidate_triangle(packed);
|
|
605
|
+
let a = read_meshlet_vertex_by_draw_index(header, triangle * 3u);
|
|
606
|
+
let b = read_meshlet_vertex_by_draw_index(header, triangle * 3u + 1u);
|
|
607
|
+
let c = read_meshlet_vertex_by_draw_index(header, triangle * 3u + 2u);
|
|
608
|
+
let tri = water_lod_triangle(a.position, b.position, c.position, a.normal, b.normal, c.normal, frame.scale);
|
|
609
|
+
let level = water_lod_candidate_level(packed);
|
|
610
|
+
let cell = water_lod_quadrant_origin(candidate.node, place >> 6u) + vec2<i32>(i32(place & 7u), i32((place >> 3u) & 7u));
|
|
611
|
+
let corners = array<vec2<f32>, 4>(
|
|
612
|
+
water_lod_lattice_point(lod, frame, level, cell),
|
|
613
|
+
water_lod_lattice_point(lod, frame, level, cell + vec2<i32>(1, 0)),
|
|
614
|
+
water_lod_lattice_point(lod, frame, level, cell + vec2<i32>(1, 1)),
|
|
615
|
+
water_lod_lattice_point(lod, frame, level, cell + vec2<i32>(0, 1)));
|
|
616
|
+
let words = water_lod_polygon_pack(water_lod_cell_clipped(tri, corners));
|
|
617
|
+
let at = water_lod_polygons_base(lod_limits) + ${WATER_LOD_POLYGON_WORDS}u * index;
|
|
618
|
+
cells[at] = words.x;
|
|
619
|
+
cells[at + 1u] = words.y;
|
|
620
|
+
}
|
|
621
|
+
`, [chunk_water_lod_candidate, chunk_water_lod_layout, chunk_water_lod_list_sums, WATER_LOD_CANDIDATE_STRUCT.declaration_chunk, chunk_lower_bound_branchless,
|
|
622
|
+
chunk_read_meshlet_vertex_by_draw_index, chunk_folded_workgroup_index]) });
|
|
623
|
+
|
|
624
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
625
|
+
|
|
626
|
+
/** Byte offsets in the command buffer the commands passes write. */
|
|
627
|
+
export const WaterLodCommand = Object.freeze({
|
|
628
|
+
/** Dispatches of the cells pass: interior, then boundary, 16 bytes apart. */
|
|
629
|
+
Cells: 0,
|
|
630
|
+
/** The flat triangles' dispatch. */
|
|
631
|
+
Flats: 32,
|
|
632
|
+
/** Draws of the interior, boundary and triangle lists, 16 bytes apart. */
|
|
633
|
+
Draws: 48,
|
|
634
|
+
/** The candidate pass's dispatch. */
|
|
635
|
+
Candidates: 96,
|
|
636
|
+
/** The cells pass's dispatch for the patch list. */
|
|
637
|
+
PatchCells: 112,
|
|
638
|
+
/** The patches' indexed draw. */
|
|
639
|
+
PatchDraw: 128,
|
|
640
|
+
});
|
|
641
|
+
export const WATER_LOD_COMMAND_BYTES = 160;
|
|
642
|
+
|
|
643
|
+
const fold = `
|
|
644
|
+
fn water_lod_dispatch(at: u32, groups: u32) {
|
|
645
|
+
const LIMIT = ${WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION}u;
|
|
646
|
+
let rows = (groups + LIMIT - 1u) / LIMIT;
|
|
647
|
+
commands[at] = min(groups, LIMIT);
|
|
648
|
+
commands[at + 1u] = max(min(rows, LIMIT), 1u);
|
|
649
|
+
commands[at + 2u] = max((rows + LIMIT - 1u) / LIMIT, 1u);
|
|
650
|
+
}
|
|
651
|
+
fn water_lod_total(sums_count: u32, last: u32) -> u32 { return select(0u, last, sums_count > 0u); }
|
|
652
|
+
fn water_lod_draw(list: u32, count: u32, vertices: u32, first: u32) {
|
|
653
|
+
let draw = ${WaterLodCommand.Draws / 4}u + 4u * list;
|
|
654
|
+
commands[draw] = vertices;
|
|
655
|
+
commands[draw + 1u] = count;
|
|
656
|
+
commands[draw + 2u] = first;
|
|
657
|
+
commands[draw + 3u] = 0u;
|
|
658
|
+
}
|
|
659
|
+
`;
|
|
660
|
+
|
|
661
|
+
const candidate_command_resources = new ShaderResourceSetDescriptor();
|
|
662
|
+
candidate_command_resources.createGroup()
|
|
663
|
+
.addUniform('limits', WATER_LOD_LIMITS_STRUCT)
|
|
664
|
+
.addStorageBuffer('triangle_sums', COLLECTION_U32_STRUCT_READ)
|
|
665
|
+
.addStorageBuffer('flat_sums', COLLECTION_U32_STRUCT_READ)
|
|
666
|
+
.addStorageBuffer('commands', WebGPUArray.u32, true)
|
|
667
|
+
.addStorageBuffer('statistics', WebGPUArray.u32, true);
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* From the triangles' scans: the candidate pass's dispatch, cut to its budget; the flat triangles' dispatch and draw;
|
|
671
|
+
* and the frame's candidate and flat triangle totals.
|
|
672
|
+
*/
|
|
673
|
+
export const shader_water_lod_candidate_command = ComputeShader.from({ label: 'Water / lod candidate command',
|
|
674
|
+
resources: candidate_command_resources, body: CodeChunk.from(/* language=WGSL */ `
|
|
675
|
+
${fold}
|
|
676
|
+
@compute @workgroup_size(1)
|
|
677
|
+
fn main() {
|
|
678
|
+
let total = water_lod_total(triangle_sums.count, triangle_sums.elements[max(triangle_sums.count, 1u) - 1u]);
|
|
679
|
+
let flats = water_lod_total(flat_sums.count, flat_sums.elements[max(flat_sums.count, 1u) - 1u]);
|
|
680
|
+
statistics[0] = total;
|
|
681
|
+
statistics[3] = flats;
|
|
682
|
+
water_lod_dispatch(${WaterLodCommand.Candidates / 4}u, (min(total, limits.candidates) + 63u) / 64u);
|
|
683
|
+
let drawn = min(flats, limits.triangles);
|
|
684
|
+
water_lod_dispatch(${WaterLodCommand.Flats / 4}u, (drawn + 63u) / 64u);
|
|
685
|
+
water_lod_draw(${WaterLodList.Triangle}u, drawn, ${WATER_LOD_TRIANGLE_VERTICES}u, ${WATER_LOD_TRIANGLE_FIRST_VERTEX}u);
|
|
686
|
+
}
|
|
687
|
+
`) });
|
|
688
|
+
|
|
689
|
+
const draw_command_resources = new ShaderResourceSetDescriptor();
|
|
690
|
+
draw_command_resources.createGroup()
|
|
691
|
+
.addUniform('limits', WATER_LOD_LIMITS_STRUCT)
|
|
692
|
+
.addStorageBuffer('sums', COLLECTION_U32_STRUCT_READ)
|
|
693
|
+
.addStorageBuffer('commands', WebGPUArray.u32, true)
|
|
694
|
+
.addStorageBuffer('statistics', WebGPUArray.u32, true);
|
|
695
|
+
|
|
696
|
+
/** The interior, boundary and patch lists' cells-pass dispatches and draws, cut to their budgets; their totals. */
|
|
697
|
+
export const shader_water_lod_draw_commands = ComputeShader.from({ label: 'Water / lod draw commands',
|
|
698
|
+
resources: draw_command_resources, body: CodeChunk.from(/* language=WGSL */ `
|
|
699
|
+
${fold}
|
|
700
|
+
fn water_lod_list(list: u32, total: u32, budget: u32, vertices: u32, first: u32) {
|
|
701
|
+
statistics[1u + list] = total;
|
|
702
|
+
let count = min(total, budget);
|
|
703
|
+
water_lod_dispatch(${WaterLodCommand.Cells / 4}u + 4u * list, (count + 63u) / 64u);
|
|
704
|
+
water_lod_draw(list, count, vertices, first);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
@compute @workgroup_size(1)
|
|
708
|
+
fn main() {
|
|
709
|
+
water_lod_list(${WaterLodList.Interior}u, water_lod_list_span(&sums.elements, sums.count, 0u).y,
|
|
710
|
+
limits.interior, ${WATER_LOD_INTERIOR_VERTICES}u, 0u);
|
|
711
|
+
water_lod_list(${WaterLodList.Boundary}u, water_lod_list_span(&sums.elements, sums.count, 1u).y,
|
|
712
|
+
limits.boundary, ${WATER_LOD_BOUNDARY_VERTICES}u, ${WATER_LOD_BOUNDARY_FIRST_VERTEX}u);
|
|
713
|
+
// patches: one instance of the patch's indices each, its points named from the patch base vertex
|
|
714
|
+
let patches = water_lod_list_span(&sums.elements, sums.count, 2u).y;
|
|
715
|
+
statistics[4] = patches;
|
|
716
|
+
let count = min(patches, limits.patches);
|
|
717
|
+
water_lod_dispatch(${WaterLodCommand.PatchCells / 4}u, (count + 63u) / 64u);
|
|
718
|
+
let draw = ${WaterLodCommand.PatchDraw / 4}u;
|
|
719
|
+
commands[draw] = ${WATER_LOD_PATCH_INDICES.length}u;
|
|
720
|
+
commands[draw + 1u] = count;
|
|
721
|
+
commands[draw + 2u] = 0u;
|
|
722
|
+
commands[draw + 3u] = ${WATER_LOD_PATCH_FIRST_VERTEX}u;
|
|
723
|
+
commands[draw + 4u] = 0u;
|
|
724
|
+
}
|
|
725
|
+
`, [chunk_water_lod_list_sums]) });
|
|
726
|
+
|
|
727
|
+
export { chunk_water_lod_candidate, chunk_water_lod_source };
|