@zephyr3d/scene 0.9.26 → 0.10.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/README.md +60 -27
- package/dist/animation/animation_bank.js +634 -0
- package/dist/animation/animation_bank.js.map +1 -0
- package/dist/animation/animation_bank_gpu.js +256 -0
- package/dist/animation/animation_bank_gpu.js.map +1 -0
- package/dist/animation/animationset.js +146 -30
- package/dist/animation/animationset.js.map +1 -1
- package/dist/animation/cloth/gpu_cloth_component.js +406 -0
- package/dist/animation/cloth/gpu_cloth_component.js.map +1 -0
- package/dist/animation/cloth/gpu_cloth_system.js +332 -202
- package/dist/animation/cloth/gpu_cloth_system.js.map +1 -1
- package/dist/animation/gpu_animation_driver.js +289 -0
- package/dist/animation/gpu_animation_driver.js.map +1 -0
- package/dist/animation/joint_dynamics/collision.js +112 -1
- package/dist/animation/joint_dynamics/collision.js.map +1 -1
- package/dist/animation/joint_dynamics/controller.js +6 -0
- package/dist/animation/joint_dynamics/controller.js.map +1 -1
- package/dist/animation/joint_dynamics/joint_dynamics_system.js +11 -0
- package/dist/animation/joint_dynamics/joint_dynamics_system.js.map +1 -1
- package/dist/animation/joint_dynamics/solver.js +50 -12
- package/dist/animation/joint_dynamics/solver.js.map +1 -1
- package/dist/animation/joint_dynamics/types.js.map +1 -1
- package/dist/animation/skeleton.js +300 -56
- package/dist/animation/skeleton.js.map +1 -1
- package/dist/animation/skin_palette_atlas.js +252 -0
- package/dist/animation/skin_palette_atlas.js.map +1 -0
- package/dist/animation/spring/multi_chain_spring_system.js +298 -38
- package/dist/animation/spring/multi_chain_spring_system.js.map +1 -1
- package/dist/animation/spring/spring_chain.js +2 -0
- package/dist/animation/spring/spring_chain.js.map +1 -1
- package/dist/animation/spring/spring_collider.js +159 -21
- package/dist/animation/spring/spring_collider.js.map +1 -1
- package/dist/animation/spring/spring_particle.js +1 -0
- package/dist/animation/spring/spring_particle.js.map +1 -1
- package/dist/animation/spring/spring_solver.js +173 -0
- package/dist/animation/spring/spring_solver.js.map +1 -0
- package/dist/animation/spring/spring_system.js +249 -24
- package/dist/animation/spring/spring_system.js.map +1 -1
- package/dist/animation/spring_modifier.js +32 -1
- package/dist/animation/spring_modifier.js.map +1 -1
- package/dist/app/app.js +26 -0
- package/dist/app/app.js.map +1 -1
- package/dist/app/engine.js +86 -15
- package/dist/app/engine.js.map +1 -1
- package/dist/app/gamepad.js +191 -0
- package/dist/app/gamepad.js.map +1 -0
- package/dist/app/inputactions.js +271 -0
- package/dist/app/inputactions.js.map +1 -0
- package/dist/app/inputmgr.js +331 -22
- package/dist/app/inputmgr.js.map +1 -1
- package/dist/app/loading.js +138 -0
- package/dist/app/loading.js.map +1 -0
- package/dist/app/pipelinecache.js +209 -0
- package/dist/app/pipelinecache.js.map +1 -0
- package/dist/app/runtimescript.js +25 -0
- package/dist/app/runtimescript.js.map +1 -1
- package/dist/app/scriptingsystem.js +54 -0
- package/dist/app/scriptingsystem.js.map +1 -1
- package/dist/app/scriptregistry.js +7 -2
- package/dist/app/scriptregistry.js.map +1 -1
- package/dist/asset/asset_manifest.js +59 -0
- package/dist/asset/asset_manifest.js.map +1 -0
- package/dist/asset/asset_preload.js +258 -0
- package/dist/asset/asset_preload.js.map +1 -0
- package/dist/asset/assetmanager.js +117 -65
- package/dist/asset/assetmanager.js.map +1 -1
- package/dist/asset/loaders/ktx2/basis/LICENSE +201 -0
- package/dist/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
- package/dist/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
- package/dist/asset/loaders/ktx2/basis_location.js +17 -0
- package/dist/asset/loaders/ktx2/basis_location.js.map +1 -0
- package/dist/asset/loaders/ktx2/ktx2_loader.js +88 -0
- package/dist/asset/loaders/ktx2/ktx2_loader.js.map +1 -0
- package/dist/asset/loaders/ktx2/ktx2_targets.js +118 -0
- package/dist/asset/loaders/ktx2/ktx2_targets.js.map +1 -0
- package/dist/asset/loaders/ktx2/transcoder_pool.js +259 -0
- package/dist/asset/loaders/ktx2/transcoder_pool.js.map +1 -0
- package/dist/asset/mesh_lod_builder.js +104 -0
- package/dist/asset/mesh_lod_builder.js.map +1 -0
- package/dist/asset/mesh_settings.js +111 -0
- package/dist/asset/mesh_settings.js.map +1 -0
- package/dist/asset/meshopt_decoder.js +18 -0
- package/dist/asset/meshopt_decoder.js.map +1 -0
- package/dist/asset/meshopt_simplifier.js +26 -0
- package/dist/asset/meshopt_simplifier.js.map +1 -0
- package/dist/asset/model.js +177 -3
- package/dist/asset/model.js.map +1 -1
- package/dist/asset/texture_settings.js +231 -0
- package/dist/asset/texture_settings.js.map +1 -0
- package/dist/asset/zmsh_binary.js +131 -0
- package/dist/asset/zmsh_binary.js.map +1 -0
- package/dist/asset/zsdf_binary.js +111 -0
- package/dist/asset/zsdf_binary.js.map +1 -0
- package/dist/blitter/bilateralblur.js +1 -2
- package/dist/blitter/bilateralblur.js.map +1 -1
- package/dist/blitter/gaussianblur.js +1 -6
- package/dist/blitter/gaussianblur.js.map +1 -1
- package/dist/camera/base.js +31 -10
- package/dist/camera/base.js.map +1 -1
- package/dist/camera/camera.js +178 -200
- package/dist/camera/camera.js.map +1 -1
- package/dist/camera/follow.js +342 -0
- package/dist/camera/follow.js.map +1 -0
- package/dist/camera/fps.js +9 -7
- package/dist/camera/fps.js.map +1 -1
- package/dist/camera/orbit.js +46 -19
- package/dist/camera/orbit.js.map +1 -1
- package/dist/index.d.ts +30475 -19865
- package/dist/index.js +67 -12
- package/dist/index.js.map +1 -1
- package/dist/material/blinn.js +3 -0
- package/dist/material/blinn.js.map +1 -1
- package/dist/material/eye.js +1 -1
- package/dist/material/eye.js.map +1 -1
- package/dist/material/hair.js +2 -3
- package/dist/material/hair.js.map +1 -1
- package/dist/material/hairstrand.js +235 -235
- package/dist/material/hairstrand.js.map +1 -1
- package/dist/material/lambert.js +4 -1
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +362 -300
- package/dist/material/material.js.map +1 -1
- package/dist/material/meshmaterial.js +118 -45
- package/dist/material/meshmaterial.js.map +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js +1 -1
- package/dist/material/mixins/lightmodel/blinnphong.js.map +1 -1
- package/dist/material/mixins/lightmodel/lambert.js +1 -1
- package/dist/material/mixins/lightmodel/lambert.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js +34 -61
- package/dist/material/mixins/lightmodel/pbrblueprintmixin.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js +16 -2
- package/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js +2 -1
- package/dist/material/mixins/lightmodel/pbrspecularglossness.js.map +1 -1
- package/dist/material/mixins/lit.js +223 -83
- package/dist/material/mixins/lit.js.map +1 -1
- package/dist/material/mixins/pbr/common.js +31 -206
- package/dist/material/mixins/pbr/common.js.map +1 -1
- package/dist/material/mtoon.js +1 -1
- package/dist/material/mtoon.js.map +1 -1
- package/dist/material/pbrmr.js +3 -0
- package/dist/material/pbrmr.js.map +1 -1
- package/dist/material/pbrsg.js +3 -0
- package/dist/material/pbrsg.js.map +1 -1
- package/dist/material/sdf_debug.js +78 -0
- package/dist/material/sdf_debug.js.map +1 -0
- package/dist/material/shader/helper.js +733 -190
- package/dist/material/shader/helper.js.map +1 -1
- package/dist/material/shader/terrain_blend.js +204 -43
- package/dist/material/shader/terrain_blend.js.map +1 -1
- package/dist/material/skin.js +331 -297
- package/dist/material/skin.js.map +1 -1
- package/dist/material/skinprofile.js +1058 -0
- package/dist/material/skinprofile.js.map +1 -0
- package/dist/material/sss.js +353 -0
- package/dist/material/sss.js.map +1 -0
- package/dist/material/sss_kernel.js +265 -0
- package/dist/material/sss_kernel.js.map +1 -0
- package/dist/material/sssprofile.js +879 -0
- package/dist/material/sssprofile.js.map +1 -0
- package/dist/material/subsurfaceprofile.js +4 -8
- package/dist/material/subsurfaceprofile.js.map +1 -1
- package/dist/material/terrain-cm.js +464 -188
- package/dist/material/terrain-cm.js.map +1 -1
- package/dist/material/unlit.js +3 -0
- package/dist/material/unlit.js.map +1 -1
- package/dist/material/water.js +2009 -201
- package/dist/material/water.js.map +1 -1
- package/dist/node_modules/meshoptimizer/LICENSE +21 -0
- package/dist/node_modules/meshoptimizer/meshopt_decoder.js +197 -0
- package/dist/node_modules/meshoptimizer/meshopt_decoder.js.map +1 -0
- package/dist/node_modules/meshoptimizer/meshopt_simplifier.js +677 -0
- package/dist/node_modules/meshoptimizer/meshopt_simplifier.js.map +1 -0
- package/dist/physics/character.js +226 -0
- package/dist/physics/character.js.map +1 -0
- package/dist/physics/collider.js +209 -0
- package/dist/physics/collider.js.map +1 -0
- package/dist/physics/events.js +79 -0
- package/dist/physics/events.js.map +1 -0
- package/dist/physics/joint.js +272 -0
- package/dist/physics/joint.js.map +1 -0
- package/dist/physics/node_physics.js +234 -0
- package/dist/physics/node_physics.js.map +1 -0
- package/dist/physics/part.js +91 -0
- package/dist/physics/part.js.map +1 -0
- package/dist/physics/rigid_body.js +379 -0
- package/dist/physics/rigid_body.js.map +1 -0
- package/dist/physics/vehicle.js +335 -0
- package/dist/physics/vehicle.js.map +1 -0
- package/dist/physics/world.js +25 -0
- package/dist/physics/world.js.map +1 -0
- package/dist/posteffect/bloom.js +49 -14
- package/dist/posteffect/bloom.js.map +1 -1
- package/dist/posteffect/motionblur.js +668 -63
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/posteffect.js +12 -1
- package/dist/posteffect/posteffect.js.map +1 -1
- package/dist/posteffect/postsss.js +572 -0
- package/dist/posteffect/postsss.js.map +1 -0
- package/dist/posteffect/sao.js +48 -50
- package/dist/posteffect/sao.js.map +1 -1
- package/dist/posteffect/skinsss.js +605 -355
- package/dist/posteffect/skinsss.js.map +1 -1
- package/dist/posteffect/ssgi.js +91 -162
- package/dist/posteffect/ssgi.js.map +1 -1
- package/dist/posteffect/ssr.js +3 -11
- package/dist/posteffect/ssr.js.map +1 -1
- package/dist/posteffect/sss.js +34 -15
- package/dist/posteffect/sss.js.map +1 -1
- package/dist/posteffect/taa.js +1 -0
- package/dist/posteffect/taa.js.map +1 -1
- package/dist/render/abuffer_oit.js +40 -39
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +328 -62
- package/dist/render/clipmap.js.map +1 -1
- package/dist/render/cluster_light.js +958 -224
- package/dist/render/cluster_light.js.map +1 -1
- package/dist/render/cull_visitor.js +26 -5
- package/dist/render/cull_visitor.js.map +1 -1
- package/dist/render/debug_views.js +37 -0
- package/dist/render/debug_views.js.map +1 -0
- package/dist/render/depthpass.js +5 -1
- package/dist/render/depthpass.js.map +1 -1
- package/dist/render/drawable_mixin.js +53 -6
- package/dist/render/drawable_mixin.js.map +1 -1
- package/dist/render/dualdepthpeeling_oit.js +53 -53
- package/dist/render/dualdepthpeeling_oit.js.map +1 -1
- package/dist/render/envlight.js +196 -113
- package/dist/render/envlight.js.map +1 -1
- package/dist/render/fbm_wavegenerator.js +8 -13
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +268 -136
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +113 -28
- package/dist/render/gerstner_wavegenerator.js.map +1 -1
- package/dist/render/gi/probe_volume.js +1334 -0
- package/dist/render/gi/probe_volume.js.map +1 -0
- package/dist/render/gi/scene_gi.js +282 -0
- package/dist/render/gi/scene_gi.js.map +1 -0
- package/dist/render/gi/voxel_atlas.js +198 -0
- package/dist/render/gi/voxel_atlas.js.map +1 -0
- package/dist/render/gi/voxel_lighting.js +652 -0
- package/dist/render/gi/voxel_lighting.js.map +1 -0
- package/dist/render/gi/voxel_scene.js +642 -0
- package/dist/render/gi/voxel_scene.js.map +1 -0
- package/dist/render/gi/voxelize_pass.js +223 -0
- package/dist/render/gi/voxelize_pass.js.map +1 -0
- package/dist/render/globalbindgroup_allocator.js +3 -2
- package/dist/render/globalbindgroup_allocator.js.map +1 -1
- package/dist/render/gpu_picking.js +9 -36
- package/dist/render/gpu_picking.js.map +1 -1
- package/dist/render/hzb.js +80 -22
- package/dist/render/hzb.js.map +1 -1
- package/dist/render/instance_culling.js +499 -0
- package/dist/render/instance_culling.js.map +1 -0
- package/dist/render/lightpass.js +23 -9
- package/dist/render/lightpass.js.map +1 -1
- package/dist/render/lod.js +46 -0
- package/dist/render/lod.js.map +1 -0
- package/dist/render/oit.js +11 -11
- package/dist/render/oit.js.map +1 -1
- package/dist/render/primitive.js +301 -9
- package/dist/render/primitive.js.map +1 -1
- package/dist/render/primitive_readback.js +209 -0
- package/dist/render/primitive_readback.js.map +1 -0
- package/dist/render/render_queue.js +198 -11
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +14 -7
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +15 -1
- package/dist/render/rendergraph/blackboard.js.map +1 -1
- package/dist/render/rendergraph/executor.js +136 -0
- package/dist/render/rendergraph/executor.js.map +1 -1
- package/dist/render/rendergraph/forward_plus_builder.js +585 -59
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/frame_resource_requirements.js +1 -0
- package/dist/render/rendergraph/frame_resource_requirements.js.map +1 -1
- package/dist/render/rendergraph/history_resources.js +8 -1
- package/dist/render/rendergraph/history_resources.js.map +1 -1
- package/dist/render/rendergraph/scene_render_context.js +31 -2
- package/dist/render/rendergraph/scene_render_context.js.map +1 -1
- package/dist/render/rendergraph/types.js.map +1 -1
- package/dist/render/renderpass.js +28 -5
- package/dist/render/renderpass.js.map +1 -1
- package/dist/render/sdf/box_allocator.js +98 -0
- package/dist/render/sdf/box_allocator.js.map +1 -0
- package/dist/render/sdf/global_sdf.js +865 -0
- package/dist/render/sdf/global_sdf.js.map +1 -0
- package/dist/render/sdf/scene_sdf.js +562 -0
- package/dist/render/sdf/scene_sdf.js.map +1 -0
- package/dist/render/sdf/sdf_atlas.js +227 -0
- package/dist/render/sdf/sdf_atlas.js.map +1 -0
- package/dist/render/sdf/sdf_programs.js +42 -0
- package/dist/render/sdf/sdf_programs.js.map +1 -0
- package/dist/render/sdf/sdf_view.js +199 -0
- package/dist/render/sdf/sdf_view.js.map +1 -0
- package/dist/render/sdf/waterline.js +610 -0
- package/dist/render/sdf/waterline.js.map +1 -0
- package/dist/render/shadow_mask_pass.js +142 -25
- package/dist/render/shadow_mask_pass.js.map +1 -1
- package/dist/render/shore_wave_generator.js +1193 -0
- package/dist/render/shore_wave_generator.js.map +1 -0
- package/dist/render/sky.js +516 -86
- package/dist/render/sky.js.map +1 -1
- package/dist/render/transmission_thickness_pass.js +477 -0
- package/dist/render/transmission_thickness_pass.js.map +1 -0
- package/dist/render/underwater.js +524 -0
- package/dist/render/underwater.js.map +1 -0
- package/dist/render/virtualtexture/virtual_texture.js +1214 -0
- package/dist/render/virtualtexture/virtual_texture.js.map +1 -0
- package/dist/render/volumetric_cloud/cloud_common.js +472 -0
- package/dist/render/volumetric_cloud/cloud_common.js.map +1 -0
- package/dist/render/volumetric_cloud/cloud_noise.js +189 -0
- package/dist/render/volumetric_cloud/cloud_noise.js.map +1 -0
- package/dist/render/volumetric_cloud/debug_view.js +131 -0
- package/dist/render/volumetric_cloud/debug_view.js.map +1 -0
- package/dist/render/volumetric_cloud/resources.js +380 -0
- package/dist/render/volumetric_cloud/resources.js.map +1 -0
- package/dist/render/volumetric_cloud/sky_cache.js +380 -0
- package/dist/render/volumetric_cloud/sky_cache.js.map +1 -0
- package/dist/render/volumetric_cloud/volumetric_cloud.js +318 -0
- package/dist/render/volumetric_cloud/volumetric_cloud.js.map +1 -0
- package/dist/render/water_caustics.js +1195 -0
- package/dist/render/water_caustics.js.map +1 -0
- package/dist/render/water_foam.js +627 -0
- package/dist/render/water_foam.js.map +1 -0
- package/dist/render/water_interaction.js +1795 -0
- package/dist/render/water_interaction.js.map +1 -0
- package/dist/render/weightedblended_oit.js +36 -36
- package/dist/render/weightedblended_oit.js.map +1 -1
- package/dist/scene/basesprite.js +9 -4
- package/dist/scene/basesprite.js.map +1 -1
- package/dist/scene/batchgroup.js +108 -12
- package/dist/scene/batchgroup.js.map +1 -1
- package/dist/scene/environment.js +28 -7
- package/dist/scene/environment.js.map +1 -1
- package/dist/scene/foliage.js +1020 -0
- package/dist/scene/foliage.js.map +1 -0
- package/dist/scene/foliage_buckets.js +147 -0
- package/dist/scene/foliage_buckets.js.map +1 -0
- package/dist/scene/foliage_mask.js +358 -0
- package/dist/scene/foliage_mask.js.map +1 -0
- package/dist/scene/hair.js +15 -4
- package/dist/scene/hair.js.map +1 -1
- package/dist/scene/light.js +179 -13
- package/dist/scene/light.js.map +1 -1
- package/dist/scene/mesh.js +327 -45
- package/dist/scene/mesh.js.map +1 -1
- package/dist/scene/meshdrawable.js +6 -0
- package/dist/scene/meshdrawable.js.map +1 -1
- package/dist/scene/msdftext.js +9 -3
- package/dist/scene/msdftext.js.map +1 -1
- package/dist/scene/msdftextsprite.js +9 -3
- package/dist/scene/msdftextsprite.js.map +1 -1
- package/dist/scene/particlesys.js +8 -2
- package/dist/scene/particlesys.js.map +1 -1
- package/dist/scene/physics_settings.js +157 -0
- package/dist/scene/physics_settings.js.map +1 -0
- package/dist/scene/scene.js +190 -44
- package/dist/scene/scene.js.map +1 -1
- package/dist/scene/scene_node.js +147 -6
- package/dist/scene/scene_node.js.map +1 -1
- package/dist/scene/terrain-cm/bladegrassmaterial.js +735 -0
- package/dist/scene/terrain-cm/bladegrassmaterial.js.map +1 -0
- package/dist/scene/terrain-cm/grass.js +881 -42
- package/dist/scene/terrain-cm/grass.js.map +1 -1
- package/dist/scene/terrain-cm/grass_gpu.js +929 -0
- package/dist/scene/terrain-cm/grass_gpu.js.map +1 -0
- package/dist/scene/terrain-cm/grassmaterial.js +180 -9
- package/dist/scene/terrain-cm/grassmaterial.js.map +1 -1
- package/dist/scene/terrain-cm/terrain-cm.js +465 -36
- package/dist/scene/terrain-cm/terrain-cm.js.map +1 -1
- package/dist/scene/terrain-cm/terrain_gi_albedo.js +257 -0
- package/dist/scene/terrain-cm/terrain_gi_albedo.js.map +1 -0
- package/dist/scene/terrain-cm/terrain_rvt.js +395 -0
- package/dist/scene/terrain-cm/terrain_rvt.js.map +1 -0
- package/dist/scene/update_queue.js +54 -0
- package/dist/scene/update_queue.js.map +1 -0
- package/dist/scene/water.js +997 -96
- package/dist/scene/water.js.map +1 -1
- package/dist/scene/water_surface_sampler.js +324 -0
- package/dist/scene/water_surface_sampler.js.map +1 -0
- package/dist/scene/wind.js +317 -0
- package/dist/scene/wind.js.map +1 -0
- package/dist/shaders/atmosphere.js +388 -166
- package/dist/shaders/atmosphere.js.map +1 -1
- package/dist/shaders/fog.js +100 -32
- package/dist/shaders/fog.js.map +1 -1
- package/dist/shaders/gi.js +365 -0
- package/dist/shaders/gi.js.map +1 -0
- package/dist/shaders/ltc_rect.js +202 -0
- package/dist/shaders/ltc_rect.js.map +1 -0
- package/dist/shaders/misc.js +1 -10
- package/dist/shaders/misc.js.map +1 -1
- package/dist/shaders/noise.js +5 -1
- package/dist/shaders/noise.js.map +1 -1
- package/dist/shaders/probes.js +309 -0
- package/dist/shaders/probes.js.map +1 -0
- package/dist/shaders/sdf.js +537 -0
- package/dist/shaders/sdf.js.map +1 -0
- package/dist/shaders/shadow.js +104 -70
- package/dist/shaders/shadow.js.map +1 -1
- package/dist/shaders/skin_brdf.js +243 -0
- package/dist/shaders/skin_brdf.js.map +1 -0
- package/dist/shaders/ssr.js +6 -159
- package/dist/shaders/ssr.js.map +1 -1
- package/dist/shaders/temporal.js +38 -27
- package/dist/shaders/temporal.js.map +1 -1
- package/dist/shaders/water.js +40 -87
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +42 -0
- package/dist/shaders/water_medium.js.map +1 -0
- package/dist/shadow/dom.js +110 -19
- package/dist/shadow/dom.js.map +1 -1
- package/dist/shadow/esm.js +3 -3
- package/dist/shadow/esm.js.map +1 -1
- package/dist/shadow/pcf_opt.js +15 -48
- package/dist/shadow/pcf_opt.js.map +1 -1
- package/dist/shadow/pcf_pd.js +15 -53
- package/dist/shadow/pcf_pd.js.map +1 -1
- package/dist/shadow/pcss.js +34 -9
- package/dist/shadow/pcss.js.map +1 -1
- package/dist/shadow/shadow_impl.js +3 -8
- package/dist/shadow/shadow_impl.js.map +1 -1
- package/dist/shadow/shadow_region.js +87 -49
- package/dist/shadow/shadow_region.js.map +1 -1
- package/dist/shadow/shadowmapper.js +93 -53
- package/dist/shadow/shadowmapper.js.map +1 -1
- package/dist/shadow/ssm.js +18 -72
- package/dist/shadow/ssm.js.map +1 -1
- package/dist/shadow/vsm.js +3 -3
- package/dist/shadow/vsm.js.map +1 -1
- package/dist/shapes/shape.js +44 -1
- package/dist/shapes/shape.js.map +1 -1
- package/dist/utility/buoyancy.js +287 -0
- package/dist/utility/buoyancy.js.map +1 -0
- package/dist/utility/physical.js +10 -1
- package/dist/utility/physical.js.map +1 -1
- package/dist/utility/pmrem.js +17 -43
- package/dist/utility/pmrem.js.map +1 -1
- package/dist/utility/sdf/bake.js +435 -0
- package/dist/utility/sdf/bake.js.map +1 -0
- package/dist/utility/sdf/bvh.js +576 -0
- package/dist/utility/sdf/bvh.js.map +1 -0
- package/dist/utility/serialization/manager.js +36 -6
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +77 -7
- package/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/utility/serialization/scene/batch.js +11 -4
- package/dist/utility/serialization/scene/batch.js.map +1 -1
- package/dist/utility/serialization/scene/camera.js +140 -279
- package/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/utility/serialization/scene/cloth.js +25 -0
- package/dist/utility/serialization/scene/cloth.js.map +1 -0
- package/dist/utility/serialization/scene/common.js +14 -6
- package/dist/utility/serialization/scene/common.js.map +1 -1
- package/dist/utility/serialization/scene/foliage.js +821 -0
- package/dist/utility/serialization/scene/foliage.js.map +1 -0
- package/dist/utility/serialization/scene/hair.js +11 -4
- package/dist/utility/serialization/scene/hair.js.map +1 -1
- package/dist/utility/serialization/scene/light.js +115 -7
- package/dist/utility/serialization/scene/light.js.map +1 -1
- package/dist/utility/serialization/scene/material.js +388 -220
- package/dist/utility/serialization/scene/material.js.map +1 -1
- package/dist/utility/serialization/scene/mesh.js +69 -4
- package/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/utility/serialization/scene/node.js +140 -5
- package/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/utility/serialization/scene/node_physics.js +1318 -0
- package/dist/utility/serialization/scene/node_physics.js.map +1 -0
- package/dist/utility/serialization/scene/particle.js +11 -4
- package/dist/utility/serialization/scene/particle.js.map +1 -1
- package/dist/utility/serialization/scene/physics.js +120 -0
- package/dist/utility/serialization/scene/physics.js.map +1 -0
- package/dist/utility/serialization/scene/primitive.js +124 -10
- package/dist/utility/serialization/scene/primitive.js.map +1 -1
- package/dist/utility/serialization/scene/scene.js +1687 -51
- package/dist/utility/serialization/scene/scene.js.map +1 -1
- package/dist/utility/serialization/scene/sprite.js +11 -4
- package/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/dist/utility/serialization/scene/terrain.js +163 -33
- package/dist/utility/serialization/scene/terrain.js.map +1 -1
- package/dist/utility/serialization/scene/text.js +11 -4
- package/dist/utility/serialization/scene/text.js.map +1 -1
- package/dist/utility/serialization/scene/water.js +2473 -114
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +97 -109
- package/dist/utility/shprojector.js.map +1 -1
- package/dist/utility/textures/foam.js +170 -0
- package/dist/utility/textures/foam.js.map +1 -0
- package/dist/utility/textures/ggxlut.js +17 -43
- package/dist/utility/textures/ggxlut.js.map +1 -1
- package/dist/utility/textures/ltcdata.js +4 -4
- package/dist/utility/textures/ltcdata.js.map +1 -1
- package/dist/utility/textures/ltclut.js +16 -6
- package/dist/utility/textures/ltclut.js.map +1 -1
- package/dist/utility/unsupported.js +24 -0
- package/dist/utility/unsupported.js.map +1 -0
- package/dist/values.js +13 -1
- package/dist/values.js.map +1 -1
- package/package.json +25 -12
- package/src/asset/loaders/ktx2/basis/LICENSE +201 -0
- package/src/asset/loaders/ktx2/basis/basis_transcoder.js +2 -0
- package/src/asset/loaders/ktx2/basis/basis_transcoder.wasm +0 -0
- package/src/asset/meshoptimizer/LICENSE +21 -0
|
@@ -0,0 +1,1193 @@
|
|
|
1
|
+
import { Disposable, Vector4, float2half } from '@zephyr3d/base';
|
|
2
|
+
import { FOAM_WAVE_GAIN } from './water_foam.js';
|
|
3
|
+
import { getDevice } from '../app/api.js';
|
|
4
|
+
import { fetchSampler } from '../utility/misc.js';
|
|
5
|
+
import { valueNoise, hash11 } from '../shaders/noise.js';
|
|
6
|
+
|
|
7
|
+
/** Texels of the inverse table along y = x - b sin x over one period, and along b. */ const INVERSE_SAMPLES = 256;
|
|
8
|
+
const INVERSE_SHARPNESS = 32;
|
|
9
|
+
/** Largest b the inverse table holds; at 1 the crest becomes a cusp. */ const SHARPNESS_MAX = 0.95;
|
|
10
|
+
/** Wave sets: noise per metre along the shore, its drift in cycles per wave, and how deep the sets cut. */ const SET_SCALE = 0.02;
|
|
11
|
+
const SET_DRIFT = 0.02;
|
|
12
|
+
const SET_DEPTH = 0.6;
|
|
13
|
+
/**
|
|
14
|
+
* Irregular waves. A point d out sees at time t the crest that left the outer
|
|
15
|
+
* edge tau(d) earlier, so the phase is Psi(t / T + tau(d) / T), with
|
|
16
|
+
* Psi(s) = s + J(s, foot): noise J in s gives each wave its own period, and
|
|
17
|
+
* noise along the shore (at the foot of each waterline point) its own bend.
|
|
18
|
+
* Psi must keep rising (1 + dJ/ds > 0) or crests would pass each other; at full
|
|
19
|
+
* irregularity dJ/ds is at most IRREGULAR_AMPLITUDE * IRREGULAR_RATE * 1.5 = 0.56.
|
|
20
|
+
*
|
|
21
|
+
* IRREGULAR_RATE: noise cycles per wave; IRREGULAR_SHORE_SCALE: noise cycles per
|
|
22
|
+
* metre along the shore; IRREGULAR_AMPLITUDE: wave cycles J spans at full irregularity.
|
|
23
|
+
*/ const IRREGULAR_RATE = 0.15;
|
|
24
|
+
const IRREGULAR_SHORE_SCALE = 0.035;
|
|
25
|
+
const IRREGULAR_AMPLITUDE = 2.5;
|
|
26
|
+
/** Step in wave cycles over which dJ/ds is taken. */ const IRREGULAR_STEP = 0.05;
|
|
27
|
+
/**
|
|
28
|
+
* Crest segments: each wave rises and falls along the shore by its own noise.
|
|
29
|
+
* Noise per metre along the shore, and how deep the gaps cut.
|
|
30
|
+
*/ const SEGMENT_SCALE = 0.06;
|
|
31
|
+
const SEGMENT_DEPTH = 0.85;
|
|
32
|
+
/**
|
|
33
|
+
* Band limit of the displacement against the mesh, after Crest's per-LOD
|
|
34
|
+
* wavelength filtering (Bowles, "Crest: Novel ocean rendering techniques in an
|
|
35
|
+
* open source framework", SIGGRAPH 2017): as mesh vertices per crest spacing
|
|
36
|
+
* fall from SHARP_SAMPLES_FULL to SHARP_SAMPLES_MIN the sharpened profile eases
|
|
37
|
+
* back to a sine; from AMP_SAMPLES_FULL to AMP_SAMPLES_MIN the wave flattens.
|
|
38
|
+
* The fragment stage keeps the full profile in its normals.
|
|
39
|
+
*/ const SHARP_SAMPLES_MIN = 4;
|
|
40
|
+
const SHARP_SAMPLES_FULL = 12;
|
|
41
|
+
const AMP_SAMPLES_MIN = 2;
|
|
42
|
+
const AMP_SAMPLES_FULL = 4;
|
|
43
|
+
/**
|
|
44
|
+
* The swash, after the ballistic run-up of Shen & Meyer ("Climb of a bore on a
|
|
45
|
+
* beach", JFM 16, 1963) in the form of Peregrine & Williams ("Swash overtopping
|
|
46
|
+
* a truncated plane beach", JFM 440, 2001): the tip leaves the waterline at u0
|
|
47
|
+
* and slows under g tan(beta), s_f = u0 t - g tan(beta) t^2 / 2, with depth
|
|
48
|
+
* h = (s_f - s)^2 / (36 g t^2) behind it. u0 = sqrt(2 g height); the run up and
|
|
49
|
+
* back lasts 2 u0 / (g tan(beta)), at most SWASH_DURATION_MAX wave periods.
|
|
50
|
+
*
|
|
51
|
+
* Water velocity u = ds_f/dt + (2/3)(s - s_f) / t, from continuity
|
|
52
|
+
* h_t + (u h)_s = 0.
|
|
53
|
+
*/ const SWASH_GRAVITY = 9.81;
|
|
54
|
+
const SWASH_DURATION_MAX = 0.8;
|
|
55
|
+
/** Metres inland over which the crest collapsing at the waterline sinks exponentially into the swash. */ const SWASH_COLLAPSE = 0.6;
|
|
56
|
+
/**
|
|
57
|
+
* Depth share of the zone (distance over zone width) past which the waves stop
|
|
58
|
+
* shortening, so crests keep at least sqrt of this times the wavelength apart.
|
|
59
|
+
*/ const MIN_COMPRESSION = 0.15;
|
|
60
|
+
/**
|
|
61
|
+
* Range of the waterline model's B (vertical over horizontal field slope,
|
|
62
|
+
* about 1 / tan of the bed angle) over which the waves fade in: none against
|
|
63
|
+
* anything steeper than about 35 degrees, full on beaches gentler than 15.
|
|
64
|
+
*/ const SHELVING_MIN = 1.5;
|
|
65
|
+
const SHELVING_FULL = 4;
|
|
66
|
+
/**
|
|
67
|
+
* Within FOAM_SLIP_BAND metres of a waterline too steep for surf (rocks, cliffs,
|
|
68
|
+
* piers; see SHELVING_MIN) foam is carried out along the wall's normal at the
|
|
69
|
+
* wall foam's drift speed, blending into the water's own velocity over that
|
|
70
|
+
* distance, so it rings the wall evenly whatever the wind. Not on a beach.
|
|
71
|
+
*/ const FOAM_SLIP_BAND = 2;
|
|
72
|
+
/**
|
|
73
|
+
* FOAM_SETTLE: the shallower the water, the less of the foam's drift it
|
|
74
|
+
* carries, by a settling coefficient k of the depth under the displaced
|
|
75
|
+
* surface, 1 at or below the settle depth (and on dry sand), 0 at and beyond
|
|
76
|
+
* the drift depth, smoothly between; the foam moves at (1 - k) of its
|
|
77
|
+
* velocity. One field and one fade over the open water, the shallows, the
|
|
78
|
+
* swash and the sand: the depth is continuous across them, so the foam is.
|
|
79
|
+
* Settled foam also fades slower, at a share of the fade rate that goes from 1
|
|
80
|
+
* to the settled foam fade with k. An empirical model with the two depths and
|
|
81
|
+
* that share as its parameters, not from a paper.
|
|
82
|
+
*/ /**
|
|
83
|
+
* The near-shore zone, see nearShore: still water shallower than the shore
|
|
84
|
+
* wave height over NEAR_SHORE_BREAKER (a wave breaks once its height reaches
|
|
85
|
+
* about 0.78 of the depth), blended into the open water over a further
|
|
86
|
+
* NEAR_SHORE_BLEND of that depth. There the surf's foam is
|
|
87
|
+
* NEAR_SHORE_FOAM_EDGE just behind a crest at the threshold, rising to
|
|
88
|
+
* NEAR_SHORE_FOAM_SHORE at the waterline and on the wet beach.
|
|
89
|
+
*/ const NEAR_SHORE_BREAKER = 0.78;
|
|
90
|
+
const NEAR_SHORE_BLEND = 0.5;
|
|
91
|
+
const NEAR_SHORE_FOAM_EDGE = 0.6;
|
|
92
|
+
const NEAR_SHORE_FOAM_SHORE = 0.85;
|
|
93
|
+
/**
|
|
94
|
+
* SURF_FOAM: within the near-shore zone each broken crest comes in as a bore
|
|
95
|
+
* with foam on its front, which fades behind it, exp(-age / duration), age
|
|
96
|
+
* being the seconds since the crest passed. Inland the swash runs on the
|
|
97
|
+
* waterline's clock, so the bore's foam goes on up the beach with it and
|
|
98
|
+
* fades as it runs up and drains: the foam surges up and back with each wave
|
|
99
|
+
* rather than lying along the shore, after the foam that rises and falls with
|
|
100
|
+
* the waves in Yang Chao's "near-shore waves" (zhuanlan.zhihu.com/p/495906664).
|
|
101
|
+
* Over the last SURF_FOAM_FRONT of a wave before its crest the foam rises,
|
|
102
|
+
* softening the bore's front. A broken wave is white water whatever its
|
|
103
|
+
* height, which sets how far it runs rather than how white it is: the foam is
|
|
104
|
+
* full from SURF_FOAM_FULL of the full height up, the wave sets and segments
|
|
105
|
+
* that weaken a wave below that only thinning it towards nothing.
|
|
106
|
+
*/ const SURF_FOAM_FRONT = 0.04;
|
|
107
|
+
const SURF_FOAM_FULL = 0.25;
|
|
108
|
+
/**
|
|
109
|
+
* Seconds over which the water's speed at a wall is averaged before it makes
|
|
110
|
+
* foam there, about a wave period: see the wall foam step.
|
|
111
|
+
*/ const WALL_SPEED_AVERAGE = 3;
|
|
112
|
+
/**
|
|
113
|
+
* Metres past FOAM_SLIP_BAND within which a cell's water is followed to where
|
|
114
|
+
* the waves carry it, about the largest horizontal displacement of choppy waves.
|
|
115
|
+
*/ const WALL_DISPLACEMENT_REACH = 6;
|
|
116
|
+
/**
|
|
117
|
+
* FOAM_WALL: foam the water makes dashing against a wall, after the trapped-air
|
|
118
|
+
* potential of Ihmsen, Akinci, Akinci & Teschner, "Unified Spray, Foam and
|
|
119
|
+
* Bubbles for Particle-Based Fluids" (CGI 2012), eq. (1)-(3) and (8):
|
|
120
|
+
* v_diff = sum |v_ij| (1 - v_ij . x_ij) W(x_ij, h), W = 1 - |x| / h, clamped by
|
|
121
|
+
* Phi(I, tmin, tmax) = (min(I, tmax) - min(I, tmin)) / (tmax - tmin), and foam
|
|
122
|
+
* made at a rate k I_k I_ta with I_k = Phi(E_k).
|
|
123
|
+
*
|
|
124
|
+
* Here the neighbour is the wall at rest, h = FOAM_SLIP_BAND along its normal n:
|
|
125
|
+
* v_diff = (|v| - v . n) W, averaged over a wave's orbit to |v| W so every face
|
|
126
|
+
* of a wall foams alike. |v| is the surface's rate of rise, which also works for
|
|
127
|
+
* generators that only displace vertically (FBM). The wall-at-rest neighbour,
|
|
128
|
+
* the orbit average, the thresholds, the per-unit-mass kinetic energy and the
|
|
129
|
+
* weighting by wall steepness are adaptations, not from the paper.
|
|
130
|
+
*
|
|
131
|
+
* The foam goes to a layer of its own (see FoamFieldSource), shown whole with
|
|
132
|
+
* its strength as opacity rather than cut from the foam texture. It is made at
|
|
133
|
+
* FOAM_WAVE_GAIN regardless of the strength, so it saturates against the wall.
|
|
134
|
+
*/ /** Largest B anything here is worked out with, a bed at about 3 degrees. */ const B_MAX = 20;
|
|
135
|
+
/** Distance in metres out from the waterline over which the wrapped generator's chop dies away. */ const CHOP_FADE = 3;
|
|
136
|
+
/**
|
|
137
|
+
* The water's edge on the beach is cut per pixel (see calcSurfaceCoverage)
|
|
138
|
+
* where the water meets the bed height of the waterline field; near the edge
|
|
139
|
+
* the mesh is held SWASH_CLEARANCE metres over the bed. That applies where the
|
|
140
|
+
* bed is at most SWASH_LIFT_MARGIN above the highest water, up to
|
|
141
|
+
* SWASH_EDGE_EXTEND metres past the farthest the water reaches; the edge is
|
|
142
|
+
* softened over SWASH_EDGE_SOFT metres.
|
|
143
|
+
*/ const SWASH_CLEARANCE = 0.06;
|
|
144
|
+
const SWASH_LIFT_MARGIN = 0.1;
|
|
145
|
+
const SWASH_EDGE_EXTEND = 2;
|
|
146
|
+
const SWASH_EDGE_SOFT = 0.08;
|
|
147
|
+
/**
|
|
148
|
+
* Share of the grid, from each edge inwards, over which the waves fade out,
|
|
149
|
+
* so the grid following the camera has no visible border.
|
|
150
|
+
*/ const GRID_EDGE_FADE = 0.15;
|
|
151
|
+
/**
|
|
152
|
+
* Levels of the shore field the waves read, at most. A level's distance to
|
|
153
|
+
* the waterline is exact only while it is shorter than the way to the level's
|
|
154
|
+
* edge (a shore off the grid may lie nearer); over SHORE_TRUST_START to 1 of
|
|
155
|
+
* that ratio a level hands over to the next coarser one, the coarsest taking
|
|
156
|
+
* what is left.
|
|
157
|
+
*/ const SHORE_LEVELS_MAX = 4;
|
|
158
|
+
const SHORE_TRUST_START = 0.5;
|
|
159
|
+
/**
|
|
160
|
+
* The foam the shore waves make feeds the water's foam field (see
|
|
161
|
+
* {@link WaterFoamField}), as Crest's UpdateFoam.compute (Wave Harmonic, MIT)
|
|
162
|
+
* is fed: by the waves where their displacement is squeezed (the determinant
|
|
163
|
+
* of its Jacobian below a coverage threshold) and by shallow water.
|
|
164
|
+
*/ let inverseTexture = null;
|
|
165
|
+
/** Names the level weights of callers that do not want them. */ let weightLocals = 0;
|
|
166
|
+
/** Names the surf out parameter of evaluate's callers that do not want it. */ let surfLocals = 0;
|
|
167
|
+
const COMPONENTS = [
|
|
168
|
+
'x',
|
|
169
|
+
'y',
|
|
170
|
+
'z',
|
|
171
|
+
'w'
|
|
172
|
+
];
|
|
173
|
+
/**
|
|
174
|
+
* The inverse of y = x - b sin x over one period, which carries the wave
|
|
175
|
+
* profile from its parameter to the distance along the wave: u is y / 2pi in
|
|
176
|
+
* [0, 1], v is b / SHARPNESS_MAX; the texel holds x / 2pi. y increases with x
|
|
177
|
+
* for b below 1, so the inverse is unique, solved here by bisection once.
|
|
178
|
+
*/ function getInverseTexture() {
|
|
179
|
+
if (!inverseTexture) {
|
|
180
|
+
const data = new Uint16Array(INVERSE_SAMPLES * INVERSE_SHARPNESS * 4);
|
|
181
|
+
for(let j = 0; j < INVERSE_SHARPNESS; j++){
|
|
182
|
+
const b = j / (INVERSE_SHARPNESS - 1) * SHARPNESS_MAX;
|
|
183
|
+
for(let i = 0; i < INVERSE_SAMPLES; i++){
|
|
184
|
+
const y = i / (INVERSE_SAMPLES - 1) * Math.PI * 2;
|
|
185
|
+
let lo = 0;
|
|
186
|
+
let hi = Math.PI * 2;
|
|
187
|
+
for(let k = 0; k < 40; k++){
|
|
188
|
+
const mid = (lo + hi) * 0.5;
|
|
189
|
+
if (mid - b * Math.sin(mid) < y) {
|
|
190
|
+
lo = mid;
|
|
191
|
+
} else {
|
|
192
|
+
hi = mid;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
const o = (j * INVERSE_SAMPLES + i) * 4;
|
|
196
|
+
data[o] = float2half((lo + hi) * 0.5 / (Math.PI * 2));
|
|
197
|
+
data[o + 3] = float2half(1);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
inverseTexture = getDevice().createTexture2D('rgba16f', INVERSE_SAMPLES, INVERSE_SHARPNESS, {
|
|
201
|
+
mipmapping: false
|
|
202
|
+
});
|
|
203
|
+
inverseTexture.name = '@ShoreWaveInverse';
|
|
204
|
+
inverseTexture.update(data, 0, 0, INVERSE_SAMPLES, INVERSE_SHARPNESS);
|
|
205
|
+
}
|
|
206
|
+
return inverseTexture;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Waves that roll in to the shore and wash up the beach, layered over another
|
|
210
|
+
* generator. After the "near-shore waves" articles by Yang Chao: a wave train
|
|
211
|
+
* whose distance along the way in is the distance to the waterline, taken from
|
|
212
|
+
* the scene's waterline field, so the crests come square on to the coast;
|
|
213
|
+
* whose wavelength shortens towards the shore; and whose profile is a
|
|
214
|
+
* trochoid, Y = A (cos x + 1) / 2 at X = (x - b sin x) / 2pi raised to the
|
|
215
|
+
* power r, sharpening (b) and leaning shoreward (r below 1) as it shoals.
|
|
216
|
+
*
|
|
217
|
+
* The profile never drops below the rest level, so each crest lifts the
|
|
218
|
+
* water over the beach and each trough lets it drain: the water's edge, cut
|
|
219
|
+
* per pixel where the water meets the bed, runs up the sand and back with the
|
|
220
|
+
* waves, along the beach's own shape.
|
|
221
|
+
*
|
|
222
|
+
* Needs the waterline field, which the water hands over every frame with
|
|
223
|
+
* {@link ShoreWaveGenerator.setFrame}; without it the waves are off and the
|
|
224
|
+
* wrapped generator shows unchanged.
|
|
225
|
+
*
|
|
226
|
+
* @public
|
|
227
|
+
*/ class ShoreWaveGenerator extends Disposable {
|
|
228
|
+
_base;
|
|
229
|
+
_settings;
|
|
230
|
+
/** The shore field's levels, and the wall field, see {@link ShoreWaveGenerator.setFrame}. */ _shore;
|
|
231
|
+
_wall;
|
|
232
|
+
_version;
|
|
233
|
+
_dummy;
|
|
234
|
+
/** Of the wall field: (grid origin x, origin z, 1 / grid size, rest level) */ _grid;
|
|
235
|
+
/** Per shore level: (grid origin x, origin z, 1 / grid size, cell size); 0 size for none. */ _shoreLevels;
|
|
236
|
+
/** (shore levels, 0, 0, 0) */ _shoreInfo;
|
|
237
|
+
/** (height, zone, crests across the zone: 2 zone / wavelength, time / period) */ _wave;
|
|
238
|
+
/** (sharpness at the shore, lean at the shore, wave clock of the previous frame, period in seconds) */ _shape;
|
|
239
|
+
/** (wave cycles the irregularity spans, wall foam drift speed, 1 / surf foam duration, 0) */ _vary;
|
|
240
|
+
/** (foam settle depth, foam drift depth, settled foam fade, 0), see FOAM_SETTLE */ _settle;
|
|
241
|
+
/** What the open water feeds the foam field with, which the shore waves add theirs to. */ _baseFoamSource;
|
|
242
|
+
/** (wave foam strength, wave foam coverage, shoreline foam strength, shoreline foam max depth) */ _foamSourceParams;
|
|
243
|
+
/** (wall foam strength, min impact speed, max impact speed, 0) */ _wallFoamParams;
|
|
244
|
+
/** The foam field step reads the shore waves under this key. */ foamSourceKey;
|
|
245
|
+
calcUnresolvedSlopeVariance;
|
|
246
|
+
constructor(base, settings, baseFoamSource){
|
|
247
|
+
super();
|
|
248
|
+
this._base = base;
|
|
249
|
+
// The shore waves are smooth at any footprint; the slopes lost are the open sea's
|
|
250
|
+
if (base.calcUnresolvedSlopeVariance) {
|
|
251
|
+
this.calcUnresolvedSlopeVariance = base.calcUnresolvedSlopeVariance.bind(base);
|
|
252
|
+
}
|
|
253
|
+
this._settings = settings;
|
|
254
|
+
this._baseFoamSource = baseFoamSource;
|
|
255
|
+
this.foamSourceKey = `SW:${baseFoamSource.foamSourceKey}`;
|
|
256
|
+
this._shore = null;
|
|
257
|
+
this._wall = null;
|
|
258
|
+
this._version = 0;
|
|
259
|
+
this._dummy = null;
|
|
260
|
+
this._grid = new Vector4(0, 0, 0, 0);
|
|
261
|
+
this._shoreLevels = Array.from({
|
|
262
|
+
length: SHORE_LEVELS_MAX
|
|
263
|
+
}, ()=>new Vector4(0, 0, 0, 0));
|
|
264
|
+
this._shoreInfo = new Vector4(0, 0, 0, 0);
|
|
265
|
+
this._wave = new Vector4(0, 1, 1, 0);
|
|
266
|
+
this._shape = new Vector4(0, 0, 0, 0);
|
|
267
|
+
this._vary = new Vector4(0, 0, 0, 0);
|
|
268
|
+
this._settle = new Vector4(0, 0, 0, 0);
|
|
269
|
+
this._foamSourceParams = new Vector4();
|
|
270
|
+
this._wallFoamParams = new Vector4();
|
|
271
|
+
}
|
|
272
|
+
/** The generator the shore waves are layered over. */ get base() {
|
|
273
|
+
return this._base;
|
|
274
|
+
}
|
|
275
|
+
/** The settings, held by reference; changes apply at the next {@link setFrame}. */ get settings() {
|
|
276
|
+
return this._settings;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Hands over the waterline fields and the clock for this frame. The shore
|
|
280
|
+
* field, in levels, shapes the waves and the beach's foam; without it the
|
|
281
|
+
* waves are off. The wall field, a single level, makes the foam against
|
|
282
|
+
* walls, rocks and piers; without it there is none.
|
|
283
|
+
* @param shore - The shore field's smoothed array, a layer per level.
|
|
284
|
+
* @param shoreRegions - Per level as the field reports it: (origin x, origin z, cell size, cells); 0 cells for none.
|
|
285
|
+
* @param wall - The wall field's smoothed array, read at layer 0.
|
|
286
|
+
* @param wallRegion - Its grid, as the shore levels'.
|
|
287
|
+
*/ setFrame(shore, shoreRegions, wall, wallRegion, level, timeInSeconds) {
|
|
288
|
+
const s = this._settings;
|
|
289
|
+
this._shore = shore;
|
|
290
|
+
this._wall = wall;
|
|
291
|
+
const levels = shore ? Math.min(shoreRegions.length, SHORE_LEVELS_MAX) : 0;
|
|
292
|
+
for(let i = 0; i < SHORE_LEVELS_MAX; i++){
|
|
293
|
+
const r = i < levels ? shoreRegions[i] : null;
|
|
294
|
+
const size = r ? r.z * r.w : 0;
|
|
295
|
+
this._shoreLevels[i].setXYZW(r?.x ?? 0, r?.y ?? 0, size > 0 ? 1 / size : 0, r?.z ?? 0);
|
|
296
|
+
}
|
|
297
|
+
this._shoreInfo.x = levels;
|
|
298
|
+
const shoreReady = levels > 0 && this._shoreLevels[0].z > 0;
|
|
299
|
+
const wallSize = wallRegion.z * wallRegion.w;
|
|
300
|
+
this._grid.setXYZW(wallRegion.x, wallRegion.y, wall && wallSize > 0 ? 1 / wallSize : 0, level);
|
|
301
|
+
const zone = Math.max(0.1, s.zone);
|
|
302
|
+
this._shape.setXYZW(Math.min(Math.max(s.sharpness, 0), SHARPNESS_MAX), Math.min(Math.max(s.lean, 0), 1), this._wave.w, Math.max(0.1, s.period));
|
|
303
|
+
this._wave.setXYZW(shoreReady ? Math.max(0, s.height) : 0, zone, 2 * zone / Math.max(0.1, s.wavelength), timeInSeconds / Math.max(0.1, s.period));
|
|
304
|
+
this._vary.x = Math.min(Math.max(s.irregularity ?? 0.6, 0), 1) * IRREGULAR_AMPLITUDE;
|
|
305
|
+
this._vary.y = Math.max(0, s.wallFoamDrift ?? 0.3);
|
|
306
|
+
this._vary.z = 1 / Math.max(0.05, s.surfFoamDuration ?? 1.5);
|
|
307
|
+
const settleDepth = Math.max(0, s.foamSettleDepth ?? 0.02);
|
|
308
|
+
this._settle.setXYZW(settleDepth, Math.max(settleDepth + 1e-3, s.foamDriftDepth ?? 0.3), Math.min(Math.max(s.settledFoamFade ?? 0.25, 0.01), 1), 0);
|
|
309
|
+
this._version++;
|
|
310
|
+
}
|
|
311
|
+
setupFoamSourceUniforms(scope) {
|
|
312
|
+
const pb = scope.$builder;
|
|
313
|
+
this._baseFoamSource.setupFoamSourceUniforms(scope);
|
|
314
|
+
this.setupShoreUniforms(scope, 0);
|
|
315
|
+
scope.swFoamSource = pb.vec4().uniform(0);
|
|
316
|
+
// (strength, min impact speed, max impact speed, 0)
|
|
317
|
+
scope.swWallFoam = pb.vec4().uniform(0);
|
|
318
|
+
}
|
|
319
|
+
applyFoamSourceBindGroup(bindGroup) {
|
|
320
|
+
const s = this._settings;
|
|
321
|
+
this._baseFoamSource.applyFoamSourceBindGroup(bindGroup);
|
|
322
|
+
this.applyShoreBindGroup(bindGroup);
|
|
323
|
+
this._foamSourceParams.setXYZW(s.waveFoamStrength ?? 1, s.waveFoamCoverage ?? 0.55, s.shorelineFoamStrength ?? 2, Math.max(0.01, s.shorelineFoamMaxDepth ?? 0.65));
|
|
324
|
+
bindGroup.setValue('swFoamSource', this._foamSourceParams);
|
|
325
|
+
const minSpeed = Math.max(0, s.wallFoamMinSpeed ?? 0.2);
|
|
326
|
+
this._wallFoamParams.setXYZW(Math.min(Math.max(s.wallFoamStrength ?? 1, 0), 1), minSpeed, Math.max(minSpeed + 1e-3, s.wallFoamMaxSpeed ?? 1.5), 0);
|
|
327
|
+
bindGroup.setValue('swWallFoam', this._wallFoamParams);
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* The velocity carrying the foam (the drift, led along walls, slowed in
|
|
331
|
+
* shallow water, see FOAM_SETTLE) and the foam the shore waves make this
|
|
332
|
+
* step: from the waves where their displacement squeezes the water and from
|
|
333
|
+
* shallow water past the surf, both prewarmed as Crest does, and from the
|
|
334
|
+
* water dashing against walls (see FOAM_WALL); added to the open water's.
|
|
335
|
+
* Raises the foam to the surf's (see SURF_FOAM) through the state's z, and
|
|
336
|
+
* keeps the wrapped generator's displacement in the field's state near walls.
|
|
337
|
+
*/ calcFoamStep(scope, xz, dt, fadeRate, prewarm, state, shape) {
|
|
338
|
+
const pb = scope.$builder;
|
|
339
|
+
const that = this;
|
|
340
|
+
pb.func('swFoamStep', [
|
|
341
|
+
pb.vec2('xz'),
|
|
342
|
+
pb.float('dt'),
|
|
343
|
+
pb.float('fadeRate'),
|
|
344
|
+
pb.bool('prewarm'),
|
|
345
|
+
pb.vec4('state').inout(),
|
|
346
|
+
pb.vec2('shape').inout()
|
|
347
|
+
], function() {
|
|
348
|
+
this.$l.baseStep = that._baseFoamSource.calcFoamStep(this, this.xz, this.dt, this.fadeRate, this.prewarm, this.state, this.shape);
|
|
349
|
+
this.$l.waveStrength = this.swFoamSource.x;
|
|
350
|
+
this.$l.dir = pb.vec4(0);
|
|
351
|
+
this.$l.spacing = pb.float(0);
|
|
352
|
+
this.$l.coverage = pb.float(1);
|
|
353
|
+
this.$l.surf = pb.vec4(0);
|
|
354
|
+
this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage, undefined, undefined, this.surf);
|
|
355
|
+
this.$l.wall = that.wall(this, this.xz);
|
|
356
|
+
this.$l.near = that.nearShore(this, this.xz);
|
|
357
|
+
this.$l.wl = pb.vec4(0);
|
|
358
|
+
this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
|
|
359
|
+
this.$l.onField = pb.and(pb.greaterThan(this.swWave.x, 0), pb.greaterThanEqual(this.gridFade, 0));
|
|
360
|
+
// Depth under the displaced surface, 0 on dry sand; deep off the field
|
|
361
|
+
this.$l.depth = pb.float(1e4);
|
|
362
|
+
this.$if(this.onField, function() {
|
|
363
|
+
this.depth = pb.max(pb.sub(this.sw.x, this.wl.z), 0);
|
|
364
|
+
});
|
|
365
|
+
// The drift, led along walls, as much of it as the depth lets carry the
|
|
366
|
+
// foam (see FOAM_SETTLE), faded out towards the edge of the field
|
|
367
|
+
this.$l.settle = pb.mul(pb.sub(1, pb.smoothStep(this.swSettle.x, this.swSettle.y, this.depth)), pb.max(this.gridFade, 0));
|
|
368
|
+
this.$l.velocity = pb.mul(that.slip(this, this.wall, this.baseStep.xy), pb.sub(1, this.settle));
|
|
369
|
+
this.$l.waveDt = this.$choice(this.prewarm, pb.max(this.dt, pb.div(pb.min(1, pb.sub(this.waveStrength, 1)), this.fadeRate)), this.dt);
|
|
370
|
+
this.$l.foam = pb.mul(FOAM_WAVE_GAIN, this.waveDt, this.waveStrength, pb.clamp(pb.sub(this.swFoamSource.y, this.sw.z), 0, 1));
|
|
371
|
+
// Foam from shallow water, by the depth under the displaced surface, as
|
|
372
|
+
// Crest makes it, scaled by the kinetic-energy potential of Ihmsen et al.
|
|
373
|
+
// (eq. (8), I_k = Phi(E_k)) of the water's speed under the shore waves,
|
|
374
|
+
// per unit mass between the wall foam's speeds: made while a crest surges
|
|
375
|
+
// in, not where the water lies still. Only where there is water, only on
|
|
376
|
+
// the waterline field, faded towards its edge as the waves are, and not
|
|
377
|
+
// in the surf, whose foam is the bores' (see SURF_FOAM): there it would
|
|
378
|
+
// gather most where the sheet lingers, at the swash's highest reach.
|
|
379
|
+
this.$if(pb.and(this.onField, pb.and(pb.greaterThan(this.depth, 0), pb.lessThan(this.near.x, 1))), function() {
|
|
380
|
+
this.$l.shoreDt = this.$choice(this.prewarm, pb.div(1, this.fadeRate), this.dt);
|
|
381
|
+
this.shoreDt = pb.mul(this.shoreDt, this.gridFade, pb.sub(1, this.near.x));
|
|
382
|
+
this.$l.shoreEnergy = pb.mul(this.sw.w, this.sw.w, 0.5);
|
|
383
|
+
this.$l.shoreEMin = pb.mul(this.swWallFoam.y, this.swWallFoam.y, 0.5);
|
|
384
|
+
this.$l.shoreEMax = pb.mul(this.swWallFoam.z, this.swWallFoam.z, 0.5);
|
|
385
|
+
this.$l.shoreKinetic = pb.div(pb.sub(pb.min(this.shoreEnergy, this.shoreEMax), pb.min(this.shoreEnergy, this.shoreEMin)), pb.max(pb.sub(this.shoreEMax, this.shoreEMin), 1e-6));
|
|
386
|
+
this.foam = pb.add(this.foam, pb.mul(this.swFoamSource.z, this.shoreDt, this.shoreKinetic, pb.clamp(pb.sub(1, pb.div(this.depth, this.swFoamSource.w)), 0, 1)));
|
|
387
|
+
});
|
|
388
|
+
// Foam from the water dashing against a wall: the trapped-air potential
|
|
389
|
+
// by the surface water's velocity (see FOAM_WALL).
|
|
390
|
+
// Cells whose water the waves can carry to within the band of a wall
|
|
391
|
+
this.$l.wallFoam = pb.float(0);
|
|
392
|
+
this.$if(pb.and(pb.greaterThan(this.wall.w, 0), pb.lessThan(this.wall.z, FOAM_SLIP_BAND + WALL_DISPLACEMENT_REACH)), function() {
|
|
393
|
+
this.$l.restPos = pb.vec3(this.xz.x, this.swGrid.w, this.xz.y);
|
|
394
|
+
this.$l.wavePos = pb.vec3(0);
|
|
395
|
+
this.$l.waveNormal = pb.vec3(0, 1, 0);
|
|
396
|
+
that._base.calcVertexPositionAndNormal(this, this.restPos, this.wavePos, this.waveNormal);
|
|
397
|
+
this.$l.rise = pb.sub(this.wavePos.y, this.restPos.y);
|
|
398
|
+
// The wall as seen from where the waves carry this water, as Crest takes
|
|
399
|
+
// the shallow water foam's depth at the displaced position
|
|
400
|
+
// (UpdateFoam.compute): the field is kept at undisplaced positions
|
|
401
|
+
this.$l.wallAt = that.wall(this, pb.add(this.xz, pb.sub(this.wavePos.xz, this.restPos.xz)));
|
|
402
|
+
this.$l.wallReach = pb.max(0, pb.sub(1, pb.div(this.wallAt.z, FOAM_SLIP_BAND)));
|
|
403
|
+
this.$l.wallW = this.$choice(pb.greaterThan(this.wallAt.z, 0), pb.mul(this.wallAt.w, this.wallReach), pb.float(0));
|
|
404
|
+
this.$l.avgSpeed = this.state.y;
|
|
405
|
+
this.$if(pb.greaterThan(this.state.w, 0), function() {
|
|
406
|
+
// Averaged over WALL_SPEED_AVERAGE seconds: the instantaneous speed
|
|
407
|
+
// varies per cell with ripples shorter than a cell
|
|
408
|
+
this.avgSpeed = pb.mix(this.state.y, pb.abs(pb.div(pb.sub(this.rise, this.state.x), this.state.w)), pb.min(1, pb.div(this.state.w, WALL_SPEED_AVERAGE)));
|
|
409
|
+
this.$l.speed = this.avgSpeed;
|
|
410
|
+
// |v| W, the trapped-air potential averaged over a wave's orbit, with
|
|
411
|
+
// W = 1 - d / h at the displaced position
|
|
412
|
+
this.$l.vDiff = pb.mul(this.speed, this.wallReach);
|
|
413
|
+
this.$l.tMin = this.swWallFoam.y;
|
|
414
|
+
this.$l.tMax = this.swWallFoam.z;
|
|
415
|
+
this.$l.trapped = pb.div(pb.sub(pb.min(this.vDiff, this.tMax), pb.min(this.vDiff, this.tMin)), pb.sub(this.tMax, this.tMin));
|
|
416
|
+
this.$l.energy = pb.mul(this.speed, this.speed, 0.5);
|
|
417
|
+
this.$l.eMin = pb.mul(this.tMin, this.tMin, 0.5);
|
|
418
|
+
this.$l.eMax = pb.mul(this.tMax, this.tMax, 0.5);
|
|
419
|
+
this.$l.kinetic = pb.div(pb.sub(pb.min(this.energy, this.eMax), pb.min(this.energy, this.eMin)), pb.max(pb.sub(this.eMax, this.eMin), 1e-6));
|
|
420
|
+
this.$if(pb.greaterThan(this.wallW, 0), function() {
|
|
421
|
+
this.wallFoam = pb.mul(FOAM_WAVE_GAIN, this.kinetic, this.trapped, this.wallAt.w, this.dt);
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
this.state = pb.vec4(this.rise, this.avgSpeed, 0, this.state.w);
|
|
425
|
+
});
|
|
426
|
+
// The surf's foam on the water there is (see SURF_FOAM): the field holds
|
|
427
|
+
// it, and where the sheet drains, keeps it on the sand (see FOAM_SETTLE),
|
|
428
|
+
// where it fades the slower the more it has settled
|
|
429
|
+
this.shape = pb.vec2(pb.mul(that.surfFoam(this, this.near, this.surf), this.coverage), pb.mix(1, this.swSettle.z, this.settle));
|
|
430
|
+
// Foam is only made where there is water: the open sea's crests run on
|
|
431
|
+
// under the beach, and their foam would surface through the sand that
|
|
432
|
+
// the field's foam is shown on (FOAM_SETTLE)
|
|
433
|
+
this.$return(pb.vec4(this.velocity, pb.mul(pb.add(this.baseStep.z, this.foam), this.coverage), pb.mul(pb.add(this.baseStep.w, this.wallFoam), this.coverage)));
|
|
434
|
+
});
|
|
435
|
+
return scope.swFoamStep(xz, dt, fadeRate, prewarm, state, shape);
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Restricts the foam projected onto the ground to the beach bed. The mesh
|
|
439
|
+
* above it only carries the draw; a scene point supplies the actual ground
|
|
440
|
+
* position.
|
|
441
|
+
*/ calcFoamGroundMask(scope, worldPos) {
|
|
442
|
+
const pb = scope.$builder;
|
|
443
|
+
const that = this;
|
|
444
|
+
pb.func('swFoamGroundMask', [
|
|
445
|
+
pb.vec3('position')
|
|
446
|
+
], function() {
|
|
447
|
+
this.$l.wl = pb.vec4(0);
|
|
448
|
+
this.$l.gridFade = that.waterlineAt(this, this.position.xz, this.wl);
|
|
449
|
+
this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
|
|
450
|
+
this.$return(pb.float(0));
|
|
451
|
+
});
|
|
452
|
+
this.$l.bed = pb.add(this.swGrid.w, this.wl.z);
|
|
453
|
+
this.$l.reach = pb.mul(this.swWave.x, pb.clamp(this.wl.y, 1, B_MAX));
|
|
454
|
+
// A bed-support mask. Not cut at the still waterline: a raised, dry
|
|
455
|
+
// carrier can project to wet ground just seaward of it, leaving neither
|
|
456
|
+
// the water nor the ground showing foam there. Sheet thickness controls
|
|
457
|
+
// the handover.
|
|
458
|
+
this.$return(pb.mul(pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y), this.gridFade, pb.sub(1, pb.smoothStep(0.08, 0.2, pb.abs(pb.sub(this.position.y, this.bed)))), this.$choice(pb.and(pb.lessThan(this.wl.z, pb.add(this.swWave.x, SWASH_LIFT_MARGIN)), pb.greaterThan(this.wl.x, pb.neg(pb.add(pb.mul(this.reach, 1.5), SWASH_EDGE_EXTEND)))), pb.float(1), pb.float(0))));
|
|
459
|
+
});
|
|
460
|
+
return scope.swFoamGroundMask(worldPos);
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* The velocity the foam texture slides with at an undisplaced XZ in the
|
|
464
|
+
* water's shaders: the drift it is given, led along the walls.
|
|
465
|
+
*/ calcFoamFlow(scope, xz, drift) {
|
|
466
|
+
const pb = scope.$builder;
|
|
467
|
+
const that = this;
|
|
468
|
+
pb.func('swFoamFlow', [
|
|
469
|
+
pb.vec2('xz'),
|
|
470
|
+
pb.vec2('drift')
|
|
471
|
+
], function() {
|
|
472
|
+
// The drift only, not the waves' water: flow mapping (Vlachos, "Water Flow
|
|
473
|
+
// in Portal 2", SIGGRAPH 2010) needs a smooth, slowly varying flow, as
|
|
474
|
+
// Crest's currents are, and the surge and swash jump at the waterline and
|
|
475
|
+
// shear along the beach. The field still carries the foam with the waves.
|
|
476
|
+
// Slowed in shallow water as the field's foam is (see FOAM_SETTLE), so
|
|
477
|
+
// the water's foam pattern and the beach's are one; by the still-water
|
|
478
|
+
// depth, which a pattern sliding at the waves' pace would tear on.
|
|
479
|
+
this.$l.wl = pb.vec4(0);
|
|
480
|
+
this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
|
|
481
|
+
this.$l.settle = pb.float(0);
|
|
482
|
+
this.$if(pb.and(pb.greaterThan(this.swWave.x, 0), pb.greaterThanEqual(this.gridFade, 0)), function() {
|
|
483
|
+
this.settle = pb.mul(pb.sub(1, pb.smoothStep(this.swSettle.x, this.swSettle.y, pb.max(pb.neg(this.wl.z), 0))), this.gridFade);
|
|
484
|
+
});
|
|
485
|
+
this.$return(pb.mul(that.slip(this, that.wall(this, this.xz), this.drift), pb.sub(1, this.settle)));
|
|
486
|
+
});
|
|
487
|
+
return scope.swFoamFlow(xz, drift);
|
|
488
|
+
}
|
|
489
|
+
/**
|
|
490
|
+
* The wall at an undisplaced XZ, from the wall field: (normal out of the
|
|
491
|
+
* land x, z, distance to the waterline, how much of a wall the waterline
|
|
492
|
+
* is, 0 on a beach the surf rolls up and off the field). The normal is only
|
|
493
|
+
* worked out within FOAM_SLIP_BAND of a wall, else 0.
|
|
494
|
+
* @internal
|
|
495
|
+
*/ wall(scope, xz) {
|
|
496
|
+
const pb = scope.$builder;
|
|
497
|
+
pb.func('swWall', [
|
|
498
|
+
pb.vec2('xz')
|
|
499
|
+
], function() {
|
|
500
|
+
this.$l.uv = pb.mul(pb.sub(this.xz, this.swGrid.xy), this.swGrid.z);
|
|
501
|
+
this.$if(pb.or(pb.lessThanEqual(this.swGrid.z, 0), pb.or(pb.any(pb.lessThan(this.uv, pb.vec2(0))), pb.any(pb.greaterThan(this.uv, pb.vec2(1))))), function() {
|
|
502
|
+
this.$return(pb.vec4(0, 0, 1e4, 0));
|
|
503
|
+
});
|
|
504
|
+
this.$l.wl = pb.textureArraySampleLevel(this.swWallField, this.uv, 0, 0);
|
|
505
|
+
this.$l.edge = pb.min(pb.min(this.uv.x, this.uv.y), pb.min(pb.sub(1, this.uv.x), pb.sub(1, this.uv.y)));
|
|
506
|
+
// The complement of the shelving the surf rolls in on, faded out towards
|
|
507
|
+
// the edge of the field
|
|
508
|
+
this.$l.steep = pb.mul(pb.sub(1, pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y)), pb.smoothStep(0, GRID_EDGE_FADE, this.edge));
|
|
509
|
+
this.$if(pb.or(pb.lessThanEqual(this.steep, 0), pb.greaterThanEqual(this.wl.x, FOAM_SLIP_BAND)), function() {
|
|
510
|
+
this.$return(pb.vec4(0, 0, this.wl.x, this.steep));
|
|
511
|
+
});
|
|
512
|
+
// Out of the land: up the gradient of the distance to the waterline, a metre either side
|
|
513
|
+
this.$l.c = this.swGrid.z;
|
|
514
|
+
this.$l.g = pb.vec2(pb.sub(pb.textureArraySampleLevel(this.swWallField, pb.add(this.uv, pb.vec2(this.c, 0)), 0, 0).x, pb.textureArraySampleLevel(this.swWallField, pb.sub(this.uv, pb.vec2(this.c, 0)), 0, 0).x), pb.sub(pb.textureArraySampleLevel(this.swWallField, pb.add(this.uv, pb.vec2(0, this.c)), 0, 0).x, pb.textureArraySampleLevel(this.swWallField, pb.sub(this.uv, pb.vec2(0, this.c)), 0, 0).x));
|
|
515
|
+
this.$return(pb.vec4(pb.div(this.g, pb.max(pb.length(this.g), 1e-5)), this.wl.x, this.steep));
|
|
516
|
+
});
|
|
517
|
+
return scope.swWall(xz);
|
|
518
|
+
}
|
|
519
|
+
/**
|
|
520
|
+
* The shore field at an undisplaced XZ: its shore coordinates (see
|
|
521
|
+
* WaterlineField.smoothTexture) into `wl`, and returned, the weight the
|
|
522
|
+
* shore effects fade by towards the edge of the field, or -1 off it or with
|
|
523
|
+
* no field. The finest level that holds the point and can vouch for its
|
|
524
|
+
* distance answers (see SHORE_LEVELS_MAX), blended into the next coarser
|
|
525
|
+
* over the last GRID_EDGE_FADE of it; only the coarsest fades out. The
|
|
526
|
+
* levels' shares go to `weights`, for {@link shoreGradient}.
|
|
527
|
+
* @internal
|
|
528
|
+
*/ waterlineAt(scope, xz, wl, weights) {
|
|
529
|
+
const pb = scope.$builder;
|
|
530
|
+
pb.func('swWaterlineAt', [
|
|
531
|
+
pb.vec2('xz'),
|
|
532
|
+
pb.vec4('wl').out(),
|
|
533
|
+
pb.vec4('weights').out()
|
|
534
|
+
], function() {
|
|
535
|
+
this.wl = pb.vec4(0);
|
|
536
|
+
this.weights = pb.vec4(0);
|
|
537
|
+
// Share of the answer still to come from coarser levels
|
|
538
|
+
this.$l.remaining = pb.float(1);
|
|
539
|
+
this.$l.last = pb.sub(this.swShoreInfo.x, 1);
|
|
540
|
+
for(let i = 0; i < SHORE_LEVELS_MAX; i++){
|
|
541
|
+
const r = this[`swShore${i}`];
|
|
542
|
+
this.$l[`uv${i}`] = pb.mul(pb.sub(this.xz, r.xy), r.z);
|
|
543
|
+
const uv = this[`uv${i}`];
|
|
544
|
+
this.$l[`edge${i}`] = pb.min(pb.min(uv.x, uv.y), pb.min(pb.sub(1, uv.x), pb.sub(1, uv.y)));
|
|
545
|
+
const edge = this[`edge${i}`];
|
|
546
|
+
this.$if(pb.and(pb.greaterThan(this.remaining, 0), pb.and(pb.greaterThan(r.z, 0), pb.greaterThan(edge, 0))), function() {
|
|
547
|
+
this.$l.t = pb.textureArraySampleLevel(this.swWaterline, uv, i, 0);
|
|
548
|
+
// Distance over the way to the edge, both in metres; the coarsest
|
|
549
|
+
// level present vouches for everything
|
|
550
|
+
this.$l.trust = this.$choice(pb.lessThan(pb.float(i), this.last), pb.sub(1, pb.smoothStep(SHORE_TRUST_START, 1, pb.div(pb.mul(pb.abs(this.t.x), r.z), edge))), pb.float(1));
|
|
551
|
+
this.$l.w = pb.mul(this.remaining, pb.smoothStep(0, GRID_EDGE_FADE, edge), this.trust);
|
|
552
|
+
this.wl = pb.add(this.wl, pb.mul(this.t, this.w));
|
|
553
|
+
this.weights[COMPONENTS[i]] = this.w;
|
|
554
|
+
this.remaining = pb.sub(this.remaining, this.w);
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
this.$l.sum = pb.sub(1, this.remaining);
|
|
558
|
+
this.$if(pb.lessThanEqual(this.sum, 0), function() {
|
|
559
|
+
this.$return(pb.float(-1));
|
|
560
|
+
});
|
|
561
|
+
this.wl = pb.div(this.wl, this.sum);
|
|
562
|
+
this.$return(this.sum);
|
|
563
|
+
});
|
|
564
|
+
if (!weights) {
|
|
565
|
+
const name = `swLevelWeights${weightLocals++}`;
|
|
566
|
+
scope.$l[name] = pb.vec4(0);
|
|
567
|
+
weights = scope[name];
|
|
568
|
+
}
|
|
569
|
+
return scope.swWaterlineAt(xz, wl, weights);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Gradient of the distance to the waterline at an undisplaced XZ, per metre,
|
|
573
|
+
* from the levels by the shares {@link waterlineAt} gave them: each level's
|
|
574
|
+
* difference a metre or a cell, whichever is more, either side.
|
|
575
|
+
* @internal
|
|
576
|
+
*/ shoreGradient(scope, xz, weights) {
|
|
577
|
+
const pb = scope.$builder;
|
|
578
|
+
pb.func('swShoreGradient', [
|
|
579
|
+
pb.vec2('xz'),
|
|
580
|
+
pb.vec4('weights')
|
|
581
|
+
], function() {
|
|
582
|
+
this.$l.g = pb.vec2(0);
|
|
583
|
+
for(let i = 0; i < SHORE_LEVELS_MAX; i++){
|
|
584
|
+
const w = this.weights[COMPONENTS[i]];
|
|
585
|
+
this.$if(pb.greaterThan(w, 0), function() {
|
|
586
|
+
const r = this[`swShore${i}`];
|
|
587
|
+
this.$l[`guv${i}`] = pb.mul(pb.sub(this.xz, r.xy), r.z);
|
|
588
|
+
this.$l[`step${i}`] = pb.max(r.w, 1);
|
|
589
|
+
this.$l[`c${i}`] = pb.mul(this[`step${i}`], r.z);
|
|
590
|
+
const uv = this[`guv${i}`];
|
|
591
|
+
const c = this[`c${i}`];
|
|
592
|
+
this.$l[`d${i}`] = pb.vec2(pb.sub(pb.textureArraySampleLevel(this.swWaterline, pb.add(uv, pb.vec2(c, 0)), i, 0).x, pb.textureArraySampleLevel(this.swWaterline, pb.sub(uv, pb.vec2(c, 0)), i, 0).x), pb.sub(pb.textureArraySampleLevel(this.swWaterline, pb.add(uv, pb.vec2(0, c)), i, 0).x, pb.textureArraySampleLevel(this.swWaterline, pb.sub(uv, pb.vec2(0, c)), i, 0).x));
|
|
593
|
+
this.g = pb.add(this.g, pb.mul(this[`d${i}`], pb.div(w, pb.mul(this[`step${i}`], 2))));
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
this.$return(this.g);
|
|
597
|
+
});
|
|
598
|
+
return scope.swShoreGradient(xz, weights);
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* A velocity near a waterline too steep for surf, given the wall there (see
|
|
602
|
+
* {@link wall}), handed over to the wall foam's drift out along the wall's
|
|
603
|
+
* normal. See FOAM_SLIP_BAND.
|
|
604
|
+
* @internal
|
|
605
|
+
*/ slip(scope, wall, v) {
|
|
606
|
+
const pb = scope.$builder;
|
|
607
|
+
pb.func('swFoamSlip', [
|
|
608
|
+
pb.vec4('wall'),
|
|
609
|
+
pb.vec2('v')
|
|
610
|
+
], function() {
|
|
611
|
+
this.$l.weight = pb.mul(this.wall.w, pb.sub(1, pb.smoothStep(0, FOAM_SLIP_BAND, this.wall.z)));
|
|
612
|
+
this.$return(pb.mix(this.v, pb.mul(this.wall.xy, this.swVary.y), this.weight));
|
|
613
|
+
});
|
|
614
|
+
return scope.swFoamSlip(wall, v);
|
|
615
|
+
}
|
|
616
|
+
clone() {
|
|
617
|
+
return new ShoreWaveGenerator(this._base.clone(), {
|
|
618
|
+
...this._settings
|
|
619
|
+
}, this._baseFoamSource);
|
|
620
|
+
}
|
|
621
|
+
get version() {
|
|
622
|
+
return this._base.version + this._version;
|
|
623
|
+
}
|
|
624
|
+
setupUniforms(scope, uniformGroup) {
|
|
625
|
+
this._base.setupUniforms(scope, uniformGroup);
|
|
626
|
+
this.setupShoreUniforms(scope, uniformGroup);
|
|
627
|
+
}
|
|
628
|
+
/** The uniforms {@link ShoreWaveGenerator.evaluate} reads. @internal */ setupShoreUniforms(scope, uniformGroup) {
|
|
629
|
+
const pb = scope.$builder;
|
|
630
|
+
// The shore field's levels, and the wall field's, see setFrame
|
|
631
|
+
scope.swWaterline = pb.tex2DArray().uniform(uniformGroup);
|
|
632
|
+
scope.swWallField = pb.tex2DArray().uniform(uniformGroup);
|
|
633
|
+
scope.swInverse = pb.tex2D().uniform(uniformGroup);
|
|
634
|
+
for(let i = 0; i < SHORE_LEVELS_MAX; i++){
|
|
635
|
+
// (origin x, origin z, 1 / size, cell size); 0 size for no level
|
|
636
|
+
scope[`swShore${i}`] = pb.vec4().uniform(uniformGroup);
|
|
637
|
+
}
|
|
638
|
+
// (shore levels, 0, 0, 0)
|
|
639
|
+
scope.swShoreInfo = pb.vec4().uniform(uniformGroup);
|
|
640
|
+
// The wall field: (origin x, origin z, 1 / size, rest level)
|
|
641
|
+
scope.swGrid = pb.vec4().uniform(uniformGroup);
|
|
642
|
+
scope.swWave = pb.vec4().uniform(uniformGroup);
|
|
643
|
+
scope.swShape = pb.vec4().uniform(uniformGroup);
|
|
644
|
+
// (irregularity: wave cycles J spans, wall foam drift speed, 1 / surf foam duration, 0)
|
|
645
|
+
scope.swVary = pb.vec4().uniform(uniformGroup);
|
|
646
|
+
// (foam settle depth, foam drift depth, settled foam fade, 0)
|
|
647
|
+
scope.swSettle = pb.vec4().uniform(uniformGroup);
|
|
648
|
+
}
|
|
649
|
+
/** @internal */ applyShoreBindGroup(bindGroup) {
|
|
650
|
+
bindGroup.setTexture('swWaterline', this._shore ?? this.getDummy(), fetchSampler('clamp_linear_nomip'));
|
|
651
|
+
bindGroup.setTexture('swWallField', this._wall ?? this.getDummy(), fetchSampler('clamp_linear_nomip'));
|
|
652
|
+
for(let i = 0; i < SHORE_LEVELS_MAX; i++){
|
|
653
|
+
bindGroup.setValue(`swShore${i}`, this._shoreLevels[i]);
|
|
654
|
+
}
|
|
655
|
+
bindGroup.setValue('swShoreInfo', this._shoreInfo);
|
|
656
|
+
bindGroup.setTexture('swInverse', getInverseTexture(), fetchSampler('clamp_linear_nomip'));
|
|
657
|
+
bindGroup.setValue('swGrid', this._grid);
|
|
658
|
+
bindGroup.setValue('swWave', this._wave);
|
|
659
|
+
bindGroup.setValue('swShape', this._shape);
|
|
660
|
+
bindGroup.setValue('swVary', this._vary);
|
|
661
|
+
bindGroup.setValue('swSettle', this._settle);
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Shore wave at an undisplaced world XZ, at a wave clock (time over period):
|
|
665
|
+
* (height over the rest level, slope of the height along the distance to
|
|
666
|
+
* the shore, determinant of the Jacobian of the displacement, speed of the
|
|
667
|
+
* water carrying the foam towards the shore in metres per second, negative
|
|
668
|
+
* in the backwash).
|
|
669
|
+
* Out parameters: `dir` (direction to the shore xy, weight of the wrapped
|
|
670
|
+
* generator's chop dying near the shore z, bed height the surface is held
|
|
671
|
+
* over w, far below the water where it is not), `spacing`, the distance
|
|
672
|
+
* between crests here, and `coverage`, the water's share of the surface.
|
|
673
|
+
* `surf`, when given, gets (seconds since the last crest passed here, on
|
|
674
|
+
* the waterline's clock inland, which the swash keeps (see SURF_FOAM); the
|
|
675
|
+
* wave's share of the full height; X, the share of a wave since the crest;
|
|
676
|
+
* the depth of the water over the bed), and (1e4, 0, 0, 0) off the shore zone.
|
|
677
|
+
* `cell` is the distance between mesh vertices to
|
|
678
|
+
* band-limit to, 0 for the full profile.
|
|
679
|
+
* @internal
|
|
680
|
+
*/ evaluate(scope, xz, dir, spacing, coverage, tPhase, cell, surf) {
|
|
681
|
+
const pb = scope.$builder;
|
|
682
|
+
const that = this;
|
|
683
|
+
pb.func('swEvaluate', [
|
|
684
|
+
pb.vec2('xz'),
|
|
685
|
+
pb.vec4('dir').out(),
|
|
686
|
+
pb.float('spacing').out(),
|
|
687
|
+
pb.float('coverage').out(),
|
|
688
|
+
pb.float('tPhase'),
|
|
689
|
+
pb.float('cell'),
|
|
690
|
+
pb.vec4('surf').out()
|
|
691
|
+
], function() {
|
|
692
|
+
// No bed to hold the surface over, unless the shore says otherwise
|
|
693
|
+
this.dir = pb.vec4(0, 0, 0, -1e4);
|
|
694
|
+
this.spacing = pb.float(1);
|
|
695
|
+
this.coverage = pb.float(1);
|
|
696
|
+
this.surf = pb.vec4(1e4, 0, 0, 0);
|
|
697
|
+
// Shore coordinates: (signed distance to the waterline, negative inland;
|
|
698
|
+
// bed B; bed height over the rest level)
|
|
699
|
+
this.$l.wl = pb.vec4(0);
|
|
700
|
+
this.$l.levelWeights = pb.vec4(0);
|
|
701
|
+
this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl, this.levelWeights);
|
|
702
|
+
this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
|
|
703
|
+
this.$return(pb.vec4(0, 0, 1, 0));
|
|
704
|
+
});
|
|
705
|
+
this.$l.d = this.wl.x;
|
|
706
|
+
this.$l.zone = this.swWave.y;
|
|
707
|
+
this.$if(pb.greaterThan(this.d, this.zone), function() {
|
|
708
|
+
this.$return(pb.vec4(0, 0, 1, 0));
|
|
709
|
+
});
|
|
710
|
+
// Direction to the shore: down the gradient of the distance
|
|
711
|
+
this.$l.g = that.shoreGradient(this, this.xz, this.levelWeights);
|
|
712
|
+
this.dir = pb.vec4(pb.neg(pb.div(this.g, pb.max(pb.length(this.g), 1e-5))), 0, 0);
|
|
713
|
+
// Phase: the integral of 1 / wavelength, with the wavelength shrinking as
|
|
714
|
+
// sqrt of the depth. Below `dMin` (past breaking) it runs on linearly;
|
|
715
|
+
// inland it is frozen at its shoreline value.
|
|
716
|
+
this.$l.dc = pb.max(this.d, 0);
|
|
717
|
+
this.$l.dMin = pb.mul(this.zone, MIN_COMPRESSION);
|
|
718
|
+
this.$l.rootMin = pb.sqrt(MIN_COMPRESSION);
|
|
719
|
+
this.$l.rootTerm = this.$choice(pb.greaterThan(this.dc, this.dMin), pb.sqrt(pb.div(this.dc, this.zone)), pb.add(this.rootMin, pb.div(pb.sub(this.dc, this.dMin), pb.mul(2, this.rootMin, this.zone))));
|
|
720
|
+
// Distance between crests here, 1 / the phase's rate of change along the way in
|
|
721
|
+
this.spacing = pb.div(pb.mul(2, pb.sqrt(pb.mul(pb.max(this.dc, this.dMin), this.zone))), pb.max(this.swWave.z, 1e-4));
|
|
722
|
+
// Where this point's way in meets the waterline: noise there varies along
|
|
723
|
+
// the shore only
|
|
724
|
+
this.$l.foot = pb.add(this.xz, pb.mul(this.dir.xy, this.d));
|
|
725
|
+
// Inland every noise is taken at the foot, so the swash keeps one clock
|
|
726
|
+
// and one height all the way up the beach
|
|
727
|
+
this.$l.noisePos = this.$choice(pb.greaterThan(this.d, 0), this.xz, this.foot);
|
|
728
|
+
// The crests' clock, in waves, and the irregular phase it reads as:
|
|
729
|
+
// s + J(s, foot), see IRREGULAR_RATE
|
|
730
|
+
this.$l.s = pb.add(this.tPhase, pb.mul(this.swWave.z, this.rootTerm));
|
|
731
|
+
this.$l.shoreUV = pb.mul(this.foot, IRREGULAR_SHORE_SCALE);
|
|
732
|
+
this.$l.j0 = valueNoise(this, pb.add(this.shoreUV, pb.vec2(pb.mul(this.s, IRREGULAR_RATE), 0)));
|
|
733
|
+
this.$l.j1 = valueNoise(this, pb.add(this.shoreUV, pb.vec2(pb.mul(pb.add(this.s, IRREGULAR_STEP), IRREGULAR_RATE), 0)));
|
|
734
|
+
this.$l.phase = pb.add(this.s, pb.mul(this.swVary.x, pb.sub(this.j0, 0.5)));
|
|
735
|
+
// dPsi/ds: a wave of a longer period here is that much farther apart
|
|
736
|
+
this.$l.rate = pb.max(pb.add(1, pb.div(pb.mul(this.swVary.x, pb.sub(this.j1, this.j0)), IRREGULAR_STEP)), 0.1);
|
|
737
|
+
// The crests travel at the speed of the water depth whatever their
|
|
738
|
+
// period: set before the spacing takes the period in
|
|
739
|
+
this.$l.crestSpeed = pb.max(pb.div(this.spacing, this.swShape.w), 0.1);
|
|
740
|
+
this.spacing = pb.div(this.spacing, this.rate);
|
|
741
|
+
// Distance along the wave from a crest, seaward: 0 at the crest, its front
|
|
742
|
+
// (shoreward) face just below 1
|
|
743
|
+
this.$l.X = pb.fract(this.phase);
|
|
744
|
+
// Shoaling, 0 at the outer edge of the zone, 1 at the waterline and inland
|
|
745
|
+
this.$l.shoal = pb.sub(1, pb.div(this.dc, this.zone));
|
|
746
|
+
// Mesh vertices per crest spacing, where a mesh is being displaced
|
|
747
|
+
this.$l.sharpLod = pb.float(1);
|
|
748
|
+
this.$l.ampLod = pb.float(1);
|
|
749
|
+
this.$if(pb.greaterThan(this.cell, 0), function() {
|
|
750
|
+
this.$l.samples = pb.div(this.spacing, this.cell);
|
|
751
|
+
this.sharpLod = pb.smoothStep(SHARP_SAMPLES_MIN, SHARP_SAMPLES_FULL, this.samples);
|
|
752
|
+
this.ampLod = pb.smoothStep(AMP_SAMPLES_MIN, AMP_SAMPLES_FULL, this.samples);
|
|
753
|
+
});
|
|
754
|
+
// The profile: X = (g / 2pi)^r with g = x - b sin x, Y = (cos x + 1) / 2.
|
|
755
|
+
// Sharper (b) and leaning further shoreward (r below 1) as it shoals.
|
|
756
|
+
this.$l.b = pb.mul(this.swShape.x, pb.smoothStep(0, 0.8, this.shoal), this.sharpLod);
|
|
757
|
+
this.$l.r = pb.div(1, pb.add(1, pb.mul(this.swShape.y, 2, pb.smoothStep(0.2, 1, this.shoal), this.sharpLod)));
|
|
758
|
+
this.$l.gu = pb.pow(pb.max(this.X, 1e-5), pb.div(1, this.r));
|
|
759
|
+
this.$l.xu = pb.textureSampleLevel(this.swInverse, pb.vec2(pb.add(pb.mul(this.gu, (INVERSE_SAMPLES - 1) / INVERSE_SAMPLES), 0.5 / INVERSE_SAMPLES), pb.add(pb.mul(pb.div(this.b, SHARPNESS_MAX), (INVERSE_SHARPNESS - 1) / INVERSE_SHARPNESS), 0.5 / INVERSE_SHARPNESS)), 0).r;
|
|
760
|
+
this.$l.x = pb.mul(this.xu, Math.PI * 2);
|
|
761
|
+
this.$l.cx = pb.cos(this.x);
|
|
762
|
+
this.$l.profile = pb.mul(pb.add(this.cx, 1), 0.5);
|
|
763
|
+
// dY/dX = (dY/dx) / (dX/dx), dX/dx = r (g / 2pi)^(r - 1) (1 - b cos x) / 2pi
|
|
764
|
+
this.$l.dXdx = pb.div(pb.mul(this.r, pb.pow(pb.max(this.gu, 1e-4), pb.sub(this.r, 1)), pb.max(pb.sub(1, pb.mul(this.b, this.cx)), 1e-3)), Math.PI * 2);
|
|
765
|
+
this.$l.dYdX = pb.div(pb.mul(pb.sin(this.x), -0.5), pb.max(this.dXdx, 1e-3));
|
|
766
|
+
// Sets: the waves swell and fade along the coast and over time
|
|
767
|
+
this.$l.waveSet = pb.sub(1, pb.mul(SET_DEPTH, pb.sub(1, pb.smoothStep(0.3, 0.7, valueNoise(this, pb.add(pb.mul(this.noisePos, SET_SCALE), pb.vec2(pb.mul(this.tPhase, SET_DRIFT), 0)))))));
|
|
768
|
+
// Segments of a wave, picked by its count: each rises and falls along the
|
|
769
|
+
// shore by its own noise
|
|
770
|
+
const segment = (scope, index)=>{
|
|
771
|
+
const seed = pb.mul(pb.vec2(hash11(scope, index), hash11(scope, pb.add(index, 17.31))), 97);
|
|
772
|
+
return pb.sub(1, pb.mul(SEGMENT_DEPTH, pb.sub(1, pb.smoothStep(0.2, 0.6, valueNoise(scope, pb.add(pb.mul(this.foot, SEGMENT_SCALE), seed))))));
|
|
773
|
+
};
|
|
774
|
+
// The swash belongs to the crest that threw it, counted from its arrival
|
|
775
|
+
// at the waterline, for the whole of its run up and back
|
|
776
|
+
this.$l.swashSet = pb.mul(this.waveSet, segment(this, pb.floor(this.phase)));
|
|
777
|
+
// Elsewhere counted from trough to trough, where the height and its slope
|
|
778
|
+
// are 0, so switching to the next wave's noise shows no seam
|
|
779
|
+
this.$l.crestSegment = segment(this, pb.floor(pb.add(this.phase, 0.5)));
|
|
780
|
+
this.waveSet = pb.mul(this.waveSet, this.crestSegment);
|
|
781
|
+
// Bed B the surf here comes in on. Inland the water comes up from the
|
|
782
|
+
// waterline, so the bed there counts too: the top of a rock or a pier
|
|
783
|
+
// standing out of the water is flat, but the water meets it at a wall
|
|
784
|
+
// and never runs up onto it as onto a beach
|
|
785
|
+
this.$l.bedSlope = this.wl.y;
|
|
786
|
+
this.$if(pb.lessThan(this.d, 0), function() {
|
|
787
|
+
this.$l.footWl = pb.vec4(0);
|
|
788
|
+
that.waterlineAt(this, this.foot, this.footWl);
|
|
789
|
+
this.bedSlope = pb.min(this.bedSlope, this.footWl.y);
|
|
790
|
+
});
|
|
791
|
+
// Only where the bed shelves (surf rolls in on a beach, not against a
|
|
792
|
+
// cliff, pier or hull), faded out towards the edge of the grid
|
|
793
|
+
this.$l.shelving = pb.mul(pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.bedSlope), this.gridFade);
|
|
794
|
+
// Rising out of the sea over the outer part of the zone
|
|
795
|
+
this.$l.amp = pb.mul(this.swWave.x, this.waveSet, this.shelving, pb.smoothStep(0, 0.4, this.shoal), this.ampLod);
|
|
796
|
+
this.$l.height = pb.mul(this.profile, this.amp);
|
|
797
|
+
// Keep the whole soft edge on the wet side of the bed intersection.
|
|
798
|
+
this.$l.edgeSoft = pb.div(SWASH_EDGE_SOFT, pb.clamp(this.bedSlope, 1, B_MAX));
|
|
799
|
+
this.coverage = pb.smoothStep(0, this.edgeSoft, pb.sub(this.height, this.wl.z));
|
|
800
|
+
// Slope along the distance to the shore: X runs seaward at 1 / spacing,
|
|
801
|
+
// and stands still inland where the phase is frozen
|
|
802
|
+
this.$l.slope = this.$choice(pb.greaterThan(this.d, 0), pb.div(pb.mul(this.dYdX, this.amp), this.spacing), pb.float(0));
|
|
803
|
+
// Determinant of the Jacobian of the horizontal displacement the profile
|
|
804
|
+
// stands for (dX/dx 2pi), below 1 where the water is squeezed at the crest's
|
|
805
|
+
// front; scaled by the wave's share of the full height.
|
|
806
|
+
this.$l.det = pb.add(1, pb.mul(pb.div(this.amp, pb.max(this.swWave.x, 1e-4)), pb.sub(pb.mul(this.dXdx, Math.PI * 2), 1)));
|
|
807
|
+
// The wrapped generator's chop dies near the shore
|
|
808
|
+
this.dir.z = pb.mul(pb.sub(1, pb.smoothStep(0, CHOP_FADE, this.d)), this.shelving);
|
|
809
|
+
// Seconds since the crest passed: the phase advances at rate / period a
|
|
810
|
+
// second, and X counts it from the crest. Inland the phase is the
|
|
811
|
+
// waterline's, so the swash's age goes on from the bore's.
|
|
812
|
+
this.surf = pb.vec4(pb.div(pb.mul(this.X, this.swShape.w), this.rate), this.$choice(pb.greaterThan(this.d, 0), pb.div(this.amp, pb.max(this.swWave.x, 1e-4)), pb.mul(this.swashSet, this.shelving)), this.X, 0);
|
|
813
|
+
// The water rises straight up and is wet wherever it stands over the bed.
|
|
814
|
+
// Near that edge the mesh is held clear of the bed and cut per pixel, only
|
|
815
|
+
// where the water could reach (not on dunes higher than the highest water)
|
|
816
|
+
// A conservative, time-independent support region: a weaker next
|
|
817
|
+
// crest must not drop the mesh under a swash that is still draining.
|
|
818
|
+
// Actual water is bounded by coverage, independently of this mesh.
|
|
819
|
+
this.$l.highest = this.swWave.x;
|
|
820
|
+
this.$l.reach = pb.mul(this.highest, pb.clamp(this.bedSlope, 1, B_MAX));
|
|
821
|
+
this.dir.w = this.$choice(pb.and(pb.and(pb.greaterThan(this.shelving, 0), pb.lessThan(this.wl.z, pb.add(this.highest, SWASH_LIFT_MARGIN))), pb.greaterThan(this.d, pb.neg(pb.add(pb.mul(this.reach, 1.5), SWASH_EDGE_EXTEND)))), this.wl.z, pb.float(-1e4));
|
|
822
|
+
// Speed of the water carrying the foam along the way in. In place of Crest's
|
|
823
|
+
// flow simulation, the velocity under a long wave, u = g eta / c with c the
|
|
824
|
+
// crest speed, towards the shore.
|
|
825
|
+
this.$l.flow = pb.clamp(pb.div(pb.mul(SWASH_GRAVITY, this.height), this.crestSpeed), 0, this.crestSpeed);
|
|
826
|
+
// Inland: the swash, in wave periods since its crest reached the waterline
|
|
827
|
+
// (X, the phase there being frozen at its waterline value)
|
|
828
|
+
this.$if(pb.lessThan(this.d, 0), function() {
|
|
829
|
+
this.$l.s = pb.neg(this.d);
|
|
830
|
+
this.$l.bedB = pb.clamp(this.bedSlope, 1, B_MAX);
|
|
831
|
+
this.$l.runUp = pb.mul(this.swWave.x, this.swashSet, this.shelving);
|
|
832
|
+
this.$l.runUpH = pb.max(pb.mul(this.runUp, this.bedB), 1e-3);
|
|
833
|
+
// Up and back in 2 u0 / (g tan(beta)) = 2 B sqrt(2 height / g) seconds
|
|
834
|
+
this.$l.duration = pb.clamp(pb.div(pb.mul(2, this.bedB, pb.sqrt(pb.div(pb.mul(2, this.runUp), SWASH_GRAVITY))), this.swShape.w), 1e-3, SWASH_DURATION_MAX);
|
|
835
|
+
this.$l.tt = pb.div(this.X, this.duration);
|
|
836
|
+
this.$l.front = this.$choice(pb.lessThan(this.tt, 1), pb.mul(4, this.runUpH, this.tt, pb.sub(1, this.tt)), pb.float(0));
|
|
837
|
+
// h = (s_f - s)^2 / (36 g' t^2) with g' tan(beta) = 8 runUpH / duration^2.
|
|
838
|
+
// The sheet climbs a bed of slope 1 / B, so a bed standing higher
|
|
839
|
+
// than that would put it here lies as far up as its height says: on
|
|
840
|
+
// a plain beach the same as the way in, and past the front on a
|
|
841
|
+
// ledge or a rock top above the reach of the swash
|
|
842
|
+
this.$l.behind = pb.sub(this.front, pb.max(this.s, pb.mul(this.wl.z, this.bedB)));
|
|
843
|
+
this.$l.depth = pb.div(pb.mul(this.behind, this.behind), pb.mul(288, this.runUpH, this.bedB, pb.max(pb.mul(this.tt, this.tt), 1e-6)));
|
|
844
|
+
// Where the sheet is not, below the rest level and the bed: never
|
|
845
|
+
// lifting the mesh, which a bed high above the water would otherwise
|
|
846
|
+
// pull up through whatever stands there
|
|
847
|
+
this.$l.swash = this.$choice(pb.greaterThan(this.behind, 0), pb.add(this.wl.z, this.depth), pb.sub(pb.min(this.wl.z, 0), 1));
|
|
848
|
+
// Its water's velocity, u = ds_f/dt + (2/3)(s - s_f) / t, in metres per
|
|
849
|
+
// second: t is tt durations of periods
|
|
850
|
+
this.$l.seconds = pb.mul(this.duration, this.swShape.w);
|
|
851
|
+
this.flow = this.$choice(pb.and(pb.greaterThan(this.behind, 0), pb.lessThan(this.tt, 1)), pb.div(pb.sub(pb.mul(4, this.runUpH, pb.sub(1, pb.mul(2, this.tt))), pb.div(pb.mul(this.behind, 2 / 3), pb.max(this.tt, 1e-3))), this.seconds), pb.float(0));
|
|
852
|
+
// The crest collapsing at the waterline
|
|
853
|
+
this.$l.collapse = pb.mul(this.height, pb.exp(pb.div(pb.neg(this.s), SWASH_COLLAPSE)));
|
|
854
|
+
// The sheet becomes quadratically thin at its front: fading by depth
|
|
855
|
+
// leaves half coverage just inside it, then cuts to zero outside.
|
|
856
|
+
// Fade over the last centimetres behind the front instead, keeping
|
|
857
|
+
// the collapsing crest's own wet region where it reaches farther.
|
|
858
|
+
this.$l.swashCoverage = this.$choice(pb.and(pb.greaterThan(this.runUp, 0), pb.and(pb.greaterThan(this.tt, 0), pb.lessThan(this.tt, 1))), pb.smoothStep(0, SWASH_EDGE_SOFT, this.behind), pb.float(0));
|
|
859
|
+
this.coverage = pb.max(this.swashCoverage, pb.smoothStep(0, this.edgeSoft, pb.sub(this.collapse, this.wl.z)));
|
|
860
|
+
this.height = pb.max(this.swash, this.collapse);
|
|
861
|
+
// The swash runs up as a sheet, not squeezed
|
|
862
|
+
this.det = pb.float(1);
|
|
863
|
+
});
|
|
864
|
+
// No surf where the bed does not shelve (a wall, a hull, a rock or box
|
|
865
|
+
// standing out of the sea): the depth test meets the water with what
|
|
866
|
+
// stands there, as without shore waves. Cutting by the bed there opens
|
|
867
|
+
// holes along the object, whose cells read a bed above the rest level
|
|
868
|
+
// up to a cell outside it.
|
|
869
|
+
this.$if(pb.lessThanEqual(this.shelving, 0), function() {
|
|
870
|
+
this.coverage = pb.float(1);
|
|
871
|
+
});
|
|
872
|
+
this.surf.w = pb.max(pb.sub(this.height, this.wl.z), 0);
|
|
873
|
+
this.$return(pb.vec4(this.height, this.slope, this.det, this.flow));
|
|
874
|
+
});
|
|
875
|
+
if (!surf) {
|
|
876
|
+
const name = `swSurfUnused${surfLocals++}`;
|
|
877
|
+
scope.$l[name] = pb.vec4(0);
|
|
878
|
+
surf = scope[name];
|
|
879
|
+
}
|
|
880
|
+
return scope.swEvaluate(xz, dir, spacing, coverage, tPhase ?? scope.swWave.w, cell ?? pb.float(0), surf);
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Adds the shore wave to a position the wrapped generator displaced, straight
|
|
884
|
+
* up, at a wave clock (time over period). Near the shore the wrapped
|
|
885
|
+
* generator's chop dies and the surface is held clear of the bed.
|
|
886
|
+
* @internal
|
|
887
|
+
*/ displace(scope, inPos, basePos, tPhase, cell) {
|
|
888
|
+
const pb = scope.$builder;
|
|
889
|
+
const that = this;
|
|
890
|
+
pb.func('swDisplace', [
|
|
891
|
+
pb.vec3('inPos'),
|
|
892
|
+
pb.vec3('basePos'),
|
|
893
|
+
pb.float('tPhase'),
|
|
894
|
+
pb.float('cell')
|
|
895
|
+
], function() {
|
|
896
|
+
this.$l.dir = pb.vec4(0);
|
|
897
|
+
this.$l.spacing = pb.float(0);
|
|
898
|
+
this.$l.coverage = pb.float(1);
|
|
899
|
+
// At the undisplaced position, where the fragment stage evaluates it too
|
|
900
|
+
this.$l.sw = that.evaluate(this, this.inPos.xz, this.dir, this.spacing, this.coverage, this.tPhase, this.cell);
|
|
901
|
+
this.$l.out = pb.mix(this.basePos, this.inPos, this.dir.z);
|
|
902
|
+
this.out.y = pb.max(pb.add(this.out.y, this.sw.x), pb.add(this.swGrid.w, this.dir.w, SWASH_CLEARANCE));
|
|
903
|
+
this.$return(this.out);
|
|
904
|
+
});
|
|
905
|
+
return scope.swDisplace(inPos, basePos, tPhase, cell);
|
|
906
|
+
}
|
|
907
|
+
calcVertexPositionAndNormal(scope, inPos, outPos, outNormal, vertexSpacing) {
|
|
908
|
+
const pb = scope.$builder;
|
|
909
|
+
const that = this;
|
|
910
|
+
pb.func('swCalcPositionAndNormal', [
|
|
911
|
+
pb.vec3('inPos'),
|
|
912
|
+
pb.vec3('outPos').out(),
|
|
913
|
+
pb.vec3('outNormal').out(),
|
|
914
|
+
pb.float('cell')
|
|
915
|
+
], function() {
|
|
916
|
+
that._base.calcVertexPositionAndNormal(this, this.inPos, this.outPos, this.outNormal);
|
|
917
|
+
this.outPos = that.displace(this, this.inPos, this.outPos, this.swWave.w, this.cell);
|
|
918
|
+
});
|
|
919
|
+
scope.swCalcPositionAndNormal(inPos, outPos, outNormal, vertexSpacing ?? pb.float(0));
|
|
920
|
+
}
|
|
921
|
+
/**
|
|
922
|
+
* Where a vertex was in the previous frame, for motion vectors: the shore wave
|
|
923
|
+
* at the previous frame's clock. The wrapped generator is taken as it is now.
|
|
924
|
+
*/ calcPrevVertexPosition(scope, inPos, outPos, vertexSpacing) {
|
|
925
|
+
const pb = scope.$builder;
|
|
926
|
+
const that = this;
|
|
927
|
+
pb.func('swCalcPrevPosition', [
|
|
928
|
+
pb.vec3('inPos'),
|
|
929
|
+
pb.vec3('outPos').out(),
|
|
930
|
+
pb.float('cell')
|
|
931
|
+
], function() {
|
|
932
|
+
this.$l.n = pb.vec3(0, 1, 0);
|
|
933
|
+
that._base.calcVertexPositionAndNormal(this, this.inPos, this.outPos, this.n);
|
|
934
|
+
this.outPos = that.displace(this, this.inPos, this.outPos, this.swShape.z, this.cell);
|
|
935
|
+
});
|
|
936
|
+
scope.swCalcPrevPosition(inPos, outPos, vertexSpacing ?? pb.float(0));
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Depth of the water over the bed of the waterline field, stable under the
|
|
940
|
+
* camera jitter. -1 where there is no bed to measure from, off the grid or
|
|
941
|
+
* away from the beach.
|
|
942
|
+
*/ calcSheetThickness(scope, xz) {
|
|
943
|
+
const pb = scope.$builder;
|
|
944
|
+
const that = this;
|
|
945
|
+
pb.func('swSheetThickness', [
|
|
946
|
+
pb.vec2('xz')
|
|
947
|
+
], function() {
|
|
948
|
+
this.$l.dir = pb.vec4(0);
|
|
949
|
+
this.$l.spacing = pb.float(0);
|
|
950
|
+
this.$l.coverage = pb.float(1);
|
|
951
|
+
this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
|
|
952
|
+
this.$if(pb.lessThan(this.dir.w, -1e3), function() {
|
|
953
|
+
this.$return(pb.float(-1));
|
|
954
|
+
});
|
|
955
|
+
this.$return(pb.max(pb.sub(this.sw.x, this.dir.w), 0));
|
|
956
|
+
});
|
|
957
|
+
return scope.swSheetThickness(xz);
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* The near-shore zone at an undisplaced XZ, as vec2(weight, foam density):
|
|
961
|
+
* weight 1 where the still water is shallower than the breaking depth and
|
|
962
|
+
* on the beach, 0 in the open water, off a beach or off the field; the
|
|
963
|
+
* density of the surf's foam just behind a crest there (see SURF_FOAM).
|
|
964
|
+
* Both depend only on the still-water depth, which is continuous from the
|
|
965
|
+
* open water across the waterline (0 inland).
|
|
966
|
+
*/ nearShore(scope, xz) {
|
|
967
|
+
const pb = scope.$builder;
|
|
968
|
+
const that = this;
|
|
969
|
+
pb.func('swNearShore', [
|
|
970
|
+
pb.vec2('xz')
|
|
971
|
+
], function() {
|
|
972
|
+
this.$l.wl = pb.vec4(0);
|
|
973
|
+
this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
|
|
974
|
+
this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
|
|
975
|
+
this.$return(pb.vec2(0));
|
|
976
|
+
});
|
|
977
|
+
// Still-water depth: the bed below the rest level, 0 inland
|
|
978
|
+
this.$l.h = pb.max(pb.neg(this.wl.z), 0);
|
|
979
|
+
this.$l.hn = pb.div(this.swWave.x, NEAR_SHORE_BREAKER);
|
|
980
|
+
this.$l.weight = pb.mul(pb.sub(1, pb.smoothStep(this.hn, pb.mul(this.hn, 1 + NEAR_SHORE_BLEND), this.h)), pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y), this.gridFade);
|
|
981
|
+
this.$return(pb.vec2(this.weight, pb.mix(NEAR_SHORE_FOAM_SHORE, NEAR_SHORE_FOAM_EDGE, pb.smoothStep(0, this.hn, this.h))));
|
|
982
|
+
});
|
|
983
|
+
return scope.swNearShore(xz);
|
|
984
|
+
}
|
|
985
|
+
/**
|
|
986
|
+
* Foam density shown at an undisplaced XZ from the field's: in the
|
|
987
|
+
* near-shore zone at least the surf's foam, which each crest brings in and
|
|
988
|
+
* which fades behind it (see SURF_FOAM).
|
|
989
|
+
*/ calcNearShoreFoam(scope, xz, density) {
|
|
990
|
+
const pb = scope.$builder;
|
|
991
|
+
const that = this;
|
|
992
|
+
pb.func('swNearShoreFoam', [
|
|
993
|
+
pb.vec2('xz'),
|
|
994
|
+
pb.float('density')
|
|
995
|
+
], function() {
|
|
996
|
+
this.$l.ns = that.nearShore(this, this.xz);
|
|
997
|
+
this.$if(pb.lessThanEqual(this.ns.x, 0), function() {
|
|
998
|
+
this.$return(this.density);
|
|
999
|
+
});
|
|
1000
|
+
this.$l.dir = pb.vec4(0);
|
|
1001
|
+
this.$l.spacing = pb.float(0);
|
|
1002
|
+
this.$l.coverage = pb.float(1);
|
|
1003
|
+
this.$l.surf = pb.vec4(0);
|
|
1004
|
+
that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage, undefined, undefined, this.surf);
|
|
1005
|
+
this.$return(pb.max(this.density, that.surfFoam(this, this.ns, this.surf)));
|
|
1006
|
+
});
|
|
1007
|
+
return scope.swNearShoreFoam(xz, density);
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Density of the surf's foam (see SURF_FOAM), from the near-shore zone (see
|
|
1011
|
+
* nearShore) and the wave's `surf` output of evaluate.
|
|
1012
|
+
* @internal
|
|
1013
|
+
*/ surfFoam(scope, near, surf) {
|
|
1014
|
+
const pb = scope.$builder;
|
|
1015
|
+
pb.func('swSurfFoam', [
|
|
1016
|
+
pb.vec2('near'),
|
|
1017
|
+
pb.vec4('surf')
|
|
1018
|
+
], function() {
|
|
1019
|
+
// The roller rises over the front face just before the crest, then fades;
|
|
1020
|
+
// the slower the shallower the water it rides up the beach on, as foam
|
|
1021
|
+
// settled there does in the field (see FOAM_SETTLE), so the swash keeps
|
|
1022
|
+
// the bore's foam rather than losing it on the way up
|
|
1023
|
+
this.$l.front = pb.smoothStep(1 - SURF_FOAM_FRONT, 1, this.surf.z);
|
|
1024
|
+
this.$l.settle = pb.sub(1, pb.smoothStep(this.swSettle.x, this.swSettle.y, this.surf.w));
|
|
1025
|
+
this.$l.rate = pb.mul(this.swVary.z, pb.mix(1, this.swSettle.z, this.settle));
|
|
1026
|
+
this.$return(pb.mul(this.near.x, this.near.y, pb.smoothStep(0, SURF_FOAM_FULL, this.surf.y), pb.max(pb.exp(pb.neg(pb.mul(this.surf.x, this.rate))), this.front)));
|
|
1027
|
+
});
|
|
1028
|
+
return scope.swSurfFoam(near, surf);
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* The water's edge on the beach, per pixel: 1 where the water stands over the
|
|
1032
|
+
* bed, 0 where the mesh is only held clear of the sand or, past where it is
|
|
1033
|
+
* held, runs down into it.
|
|
1034
|
+
*/ calcSurfaceCoverage(scope, xz) {
|
|
1035
|
+
const pb = scope.$builder;
|
|
1036
|
+
const that = this;
|
|
1037
|
+
pb.func('swSurfaceCoverage', [
|
|
1038
|
+
pb.vec2('xz')
|
|
1039
|
+
], function() {
|
|
1040
|
+
this.$l.dir = pb.vec4(0);
|
|
1041
|
+
this.$l.spacing = pb.float(0);
|
|
1042
|
+
this.$l.coverage = pb.float(1);
|
|
1043
|
+
that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
|
|
1044
|
+
this.$return(this.coverage);
|
|
1045
|
+
});
|
|
1046
|
+
return scope.swSurfaceCoverage(xz);
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* Debug classification on the same undisplaced coordinates as the mesh:
|
|
1050
|
+
* (shore envelope weight, inland flag, current wet coverage). The envelope
|
|
1051
|
+
* excludes the moving crest profile and wave-set strength, so its boundary
|
|
1052
|
+
* remains legible throughout a wave cycle. This is one displaced mesh.
|
|
1053
|
+
* @internal
|
|
1054
|
+
*/ calcDebugRegion(scope, xz) {
|
|
1055
|
+
const pb = scope.$builder;
|
|
1056
|
+
const that = this;
|
|
1057
|
+
pb.func('swDebugRegion', [
|
|
1058
|
+
pb.vec2('xz')
|
|
1059
|
+
], function() {
|
|
1060
|
+
this.$l.wl = pb.vec4(0);
|
|
1061
|
+
this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl);
|
|
1062
|
+
this.$if(pb.or(pb.lessThanEqual(this.swWave.x, 0), pb.lessThan(this.gridFade, 0)), function() {
|
|
1063
|
+
this.$return(pb.vec3(0, 0, 1));
|
|
1064
|
+
});
|
|
1065
|
+
// Match evaluate's shelving, grid-edge and outer-zone amplitude fades;
|
|
1066
|
+
// dir.z alone only measures the much narrower chop-suppression band.
|
|
1067
|
+
this.$l.weight = pb.mul(pb.smoothStep(SHELVING_MIN, SHELVING_FULL, this.wl.y), this.gridFade, pb.smoothStep(0, 0.4, pb.sub(1, pb.div(pb.max(this.wl.x, 0), this.swWave.y))));
|
|
1068
|
+
this.$l.dir = pb.vec4(0);
|
|
1069
|
+
this.$l.spacing = pb.float(0);
|
|
1070
|
+
this.$l.coverage = pb.float(1);
|
|
1071
|
+
that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
|
|
1072
|
+
this.$return(pb.vec3(this.weight, this.$choice(pb.lessThan(this.wl.x, 0), pb.float(1), pb.float(0)), this.coverage));
|
|
1073
|
+
});
|
|
1074
|
+
return scope.swDebugRegion(xz);
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Debug colour of the shore field's levels at an undisplaced XZ: red, green,
|
|
1078
|
+
* blue and yellow from the finest by their shares, times the fade out of
|
|
1079
|
+
* the coarsest, banded every 2 metres of the distance to the waterline.
|
|
1080
|
+
* @internal
|
|
1081
|
+
*/ calcDebugLevels(scope, xz) {
|
|
1082
|
+
const pb = scope.$builder;
|
|
1083
|
+
const that = this;
|
|
1084
|
+
pb.func('swDebugLevels', [
|
|
1085
|
+
pb.vec2('xz')
|
|
1086
|
+
], function() {
|
|
1087
|
+
this.$l.wl = pb.vec4(0);
|
|
1088
|
+
this.$l.levelWeights = pb.vec4(0);
|
|
1089
|
+
this.$l.gridFade = that.waterlineAt(this, this.xz, this.wl, this.levelWeights);
|
|
1090
|
+
this.$if(pb.lessThan(this.gridFade, 0), function() {
|
|
1091
|
+
this.$return(pb.vec3(0.1));
|
|
1092
|
+
});
|
|
1093
|
+
this.$l.c = pb.div(pb.add(pb.add(pb.mul(pb.vec3(1, 0.1, 0.1), this.levelWeights.x), pb.mul(pb.vec3(0.1, 1, 0.1), this.levelWeights.y)), pb.add(pb.mul(pb.vec3(0.15, 0.3, 1), this.levelWeights.z), pb.mul(pb.vec3(1, 0.9, 0.1), this.levelWeights.w))), this.gridFade);
|
|
1094
|
+
this.$l.band = pb.step(0.5, pb.fract(pb.mul(this.wl.x, 0.5)));
|
|
1095
|
+
this.$return(pb.mul(this.c, this.gridFade, pb.mix(0.55, 1, this.band)));
|
|
1096
|
+
});
|
|
1097
|
+
return scope.swDebugLevels(xz);
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* Normal tilted by the shore wave's slope, which runs along the direction to
|
|
1101
|
+
* the shore: the analytic dY/dX of the profile, so no extra evaluations.
|
|
1102
|
+
* @internal
|
|
1103
|
+
*/ tilt(scope, n, sw, dir) {
|
|
1104
|
+
const pb = scope.$builder;
|
|
1105
|
+
// Gradient of the height in XZ: the slope along the distance, which grows seaward (-dir)
|
|
1106
|
+
const grad = pb.mul(pb.neg(dir.xy), sw.y);
|
|
1107
|
+
return pb.normalize(pb.add(pb.normalize(n), pb.vec3(pb.neg(grad.x), 0, pb.neg(grad.y))));
|
|
1108
|
+
}
|
|
1109
|
+
calcFragmentNormal(scope, xz, vertexNormal) {
|
|
1110
|
+
const pb = scope.$builder;
|
|
1111
|
+
const that = this;
|
|
1112
|
+
pb.func('swCalcFragmentNormal', [
|
|
1113
|
+
pb.vec2('xz'),
|
|
1114
|
+
pb.vec3('vn')
|
|
1115
|
+
], function() {
|
|
1116
|
+
this.$l.dir = pb.vec4(0);
|
|
1117
|
+
this.$l.spacing = pb.float(0);
|
|
1118
|
+
this.$l.coverage = pb.float(1);
|
|
1119
|
+
this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
|
|
1120
|
+
this.$l.n = that._base.calcFragmentNormal(this, this.xz, this.vn);
|
|
1121
|
+
this.$return(that.tilt(this, this.n, this.sw, this.dir));
|
|
1122
|
+
});
|
|
1123
|
+
return scope.swCalcFragmentNormal(xz, vertexNormal);
|
|
1124
|
+
}
|
|
1125
|
+
calcFragmentNormalAndFoam(scope, xz, vertexNormal, foamSample, footprint) {
|
|
1126
|
+
const pb = scope.$builder;
|
|
1127
|
+
const that = this;
|
|
1128
|
+
const params = [
|
|
1129
|
+
pb.vec2('xz'),
|
|
1130
|
+
pb.vec3('vn')
|
|
1131
|
+
];
|
|
1132
|
+
const args = [
|
|
1133
|
+
xz,
|
|
1134
|
+
vertexNormal
|
|
1135
|
+
];
|
|
1136
|
+
if (foamSample) {
|
|
1137
|
+
params.push(pb.vec3('fs').inout());
|
|
1138
|
+
args.push(foamSample);
|
|
1139
|
+
}
|
|
1140
|
+
if (footprint) {
|
|
1141
|
+
params.push(pb.vec4('fp'));
|
|
1142
|
+
args.push(footprint);
|
|
1143
|
+
}
|
|
1144
|
+
const funcName = `swCalcNormalAndFoam${foamSample ? 'S' : ''}${footprint ? 'F' : ''}`;
|
|
1145
|
+
pb.func(funcName, params, function() {
|
|
1146
|
+
this.$l.dir = pb.vec4(0);
|
|
1147
|
+
this.$l.spacing = pb.float(0);
|
|
1148
|
+
this.$l.coverage = pb.float(1);
|
|
1149
|
+
this.$l.sw = that.evaluate(this, this.xz, this.dir, this.spacing, this.coverage);
|
|
1150
|
+
// The foam the shore waves make is in the foam field, which the wrapped
|
|
1151
|
+
// generator reads
|
|
1152
|
+
this.$l.nf = that._base.calcFragmentNormalAndFoam(this, this.xz, this.vn, foamSample ? this.fs : undefined, footprint ? this.fp : undefined);
|
|
1153
|
+
this.$return(pb.vec4(that.tilt(this, this.nf.xyz, this.sw, this.dir), this.nf.w));
|
|
1154
|
+
});
|
|
1155
|
+
return scope[funcName](...args);
|
|
1156
|
+
}
|
|
1157
|
+
applyWaterBindGroup(bindGroup) {
|
|
1158
|
+
this._base.applyWaterBindGroup(bindGroup);
|
|
1159
|
+
this.applyShoreBindGroup(bindGroup);
|
|
1160
|
+
}
|
|
1161
|
+
calcClipmapTileAABB(minX, maxX, minZ, maxZ, y, outAABB) {
|
|
1162
|
+
this._base.calcClipmapTileAABB(minX, maxX, minZ, maxZ, y, outAABB);
|
|
1163
|
+
// The water rises by up to the wave height, and near its edge on the beach
|
|
1164
|
+
// the surface is held over the bed, a little higher still
|
|
1165
|
+
outAABB.maxPoint.y += this._settings.height + SWASH_LIFT_MARGIN + SWASH_CLEARANCE;
|
|
1166
|
+
}
|
|
1167
|
+
/** The material steps the base generator; the clock comes with {@link setFrame}. */ update() {}
|
|
1168
|
+
getHash() {
|
|
1169
|
+
return `${this._base.getHash()}:SW`;
|
|
1170
|
+
}
|
|
1171
|
+
needUpdate() {
|
|
1172
|
+
return true;
|
|
1173
|
+
}
|
|
1174
|
+
isOk(device) {
|
|
1175
|
+
return this._base.isOk(device);
|
|
1176
|
+
}
|
|
1177
|
+
onDispose() {
|
|
1178
|
+
super.onDispose();
|
|
1179
|
+
this._dummy?.dispose();
|
|
1180
|
+
this._dummy = null;
|
|
1181
|
+
}
|
|
1182
|
+
/** @internal */ getDummy() {
|
|
1183
|
+
if (!this._dummy) {
|
|
1184
|
+
this._dummy = getDevice().createTexture2DArray('rgba16f', 1, 1, 1, {
|
|
1185
|
+
mipmapping: false
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
1188
|
+
return this._dummy;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
export { ShoreWaveGenerator };
|
|
1193
|
+
//# sourceMappingURL=shore_wave_generator.js.map
|