@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,865 @@
|
|
|
1
|
+
import { Vector3, Vector4 } from '@zephyr3d/base';
|
|
2
|
+
import { getDevice } from '../../app/api.js';
|
|
3
|
+
import { fetchSampler } from '../../utility/misc.js';
|
|
4
|
+
import { getDefaultTexture2D } from '../../utility/blueprint/material/texture.js';
|
|
5
|
+
import { MAX_DYNAMIC_SDF_INSTANCES, SDF_INSTANCE_VEC4S, globalSDFFormat, SDF_ATLAS_TEXTURE, SDF_DYNAMIC_INSTANCES, SDF_DYNAMIC_INFO, GLOBAL_SDF_TEXTURE, isGlobalSDFFilterable, GLOBAL_SDF_TAGS, GLOBAL_SDF_LEVELS, GLOBAL_SDF_PARAMS, GLOBAL_SDF_BRICK, sampleInstancesSDF, GLOBAL_SDF_TAG_NOT_COVERED, declareGlobalSDFUniforms, declareDynamicSDFUniforms, sampleSceneSDF } from '../../shaders/sdf.js';
|
|
6
|
+
import { WaterlineField, SDFWaterlineSource } from './waterline.js';
|
|
7
|
+
import { getSDFProgramErrors, buildSDFProgram } from './sdf_programs.js';
|
|
8
|
+
import { sampleInstanceMaterial } from '../../shaders/gi.js';
|
|
9
|
+
import { RenderGraphExecutor } from '../rendergraph/executor.js';
|
|
10
|
+
|
|
11
|
+
/** Voxels along a brick edge, the unit of compositing. */ const BRICK = GLOBAL_SDF_BRICK;
|
|
12
|
+
/** vec4s per brick in the brick buffer. */ const BRICK_VEC4S = 3;
|
|
13
|
+
/** Most points per {@link GlobalSDF.queryDistances} call. */ const MAX_QUERY_POINTS = 4096;
|
|
14
|
+
/**
|
|
15
|
+
* Writes an instance in the layout of {@link SDF_INSTANCE_VEC4S}: world-to-local rows, field box
|
|
16
|
+
* with voxel size and world scale, atlas slot with the two-sided half thickness (half a field
|
|
17
|
+
* voxel in world units; -1 for one-sided). The scale is the smallest axis scale so scaled field
|
|
18
|
+
* values never overshoot under non-uniform scale.
|
|
19
|
+
*/ function packSDFInstance(inst, atlas, out, offset) {
|
|
20
|
+
const header = inst.asset.header;
|
|
21
|
+
const slot = atlas.getSlot(inst.asset);
|
|
22
|
+
const inv = inst.mesh.invWorldMatrix;
|
|
23
|
+
const m = inst.mesh.worldMatrix;
|
|
24
|
+
const scale = Math.min(Math.hypot(m.m00, m.m10, m.m20), Math.hypot(m.m01, m.m11, m.m21), Math.hypot(m.m02, m.m12, m.m22));
|
|
25
|
+
out.set([
|
|
26
|
+
inv.m00,
|
|
27
|
+
inv.m01,
|
|
28
|
+
inv.m02,
|
|
29
|
+
inv.m03
|
|
30
|
+
], offset);
|
|
31
|
+
out.set([
|
|
32
|
+
inv.m10,
|
|
33
|
+
inv.m11,
|
|
34
|
+
inv.m12,
|
|
35
|
+
inv.m13
|
|
36
|
+
], offset + 4);
|
|
37
|
+
out.set([
|
|
38
|
+
inv.m20,
|
|
39
|
+
inv.m21,
|
|
40
|
+
inv.m22,
|
|
41
|
+
inv.m23
|
|
42
|
+
], offset + 8);
|
|
43
|
+
out.set([
|
|
44
|
+
...header.boxMin,
|
|
45
|
+
header.voxelSize
|
|
46
|
+
], offset + 12);
|
|
47
|
+
out.set([
|
|
48
|
+
...header.boxMax,
|
|
49
|
+
scale
|
|
50
|
+
], offset + 16);
|
|
51
|
+
out.set([
|
|
52
|
+
...slot.offset,
|
|
53
|
+
header.twoSided ? 0.5 * header.voxelSize * scale : -1
|
|
54
|
+
], offset + 20);
|
|
55
|
+
}
|
|
56
|
+
const tmpDirtyMin = new Vector3();
|
|
57
|
+
const tmpDirtyMax = new Vector3();
|
|
58
|
+
/**
|
|
59
|
+
* Camera-centred clipmap of signed distance (`levels` cubes of `resolution`³ voxels, each level
|
|
60
|
+
* doubling the voxel size), composited on the GPU from the static instances of a {@link SceneSDF}
|
|
61
|
+
* and the terrain height map. WebGPU only.
|
|
62
|
+
*
|
|
63
|
+
* Levels are toroidally addressed (world voxel `v` at texel `v mod resolution`) and updated
|
|
64
|
+
* incrementally in dirty 8³ bricks, a budget per frame, coarsest level first. Distances are
|
|
65
|
+
* clamped to a band of a few voxels per level; {@link sampleGlobalSDF} falls back to coarser
|
|
66
|
+
* levels outside it.
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/ class GlobalSDF {
|
|
70
|
+
levels;
|
|
71
|
+
resolution;
|
|
72
|
+
baseVoxelSize;
|
|
73
|
+
bandVoxels;
|
|
74
|
+
brickBudget;
|
|
75
|
+
_sdf;
|
|
76
|
+
_texture;
|
|
77
|
+
_tags;
|
|
78
|
+
/** Window minimum of each level in voxels of that level; null before the first update. */ _origins;
|
|
79
|
+
/** Dirty bricks of each level, keyed by world brick coordinates. */ _dirty;
|
|
80
|
+
_lastBricks;
|
|
81
|
+
_freshBricks;
|
|
82
|
+
_freshCount;
|
|
83
|
+
_totalBricks;
|
|
84
|
+
_terrain;
|
|
85
|
+
_terrainVersion;
|
|
86
|
+
_program;
|
|
87
|
+
_bindGroup;
|
|
88
|
+
_instanceBuffer;
|
|
89
|
+
_brickBuffer;
|
|
90
|
+
_indexBuffer;
|
|
91
|
+
_materialBuffer;
|
|
92
|
+
_materials;
|
|
93
|
+
_dummyHeightMap;
|
|
94
|
+
_queryProgram;
|
|
95
|
+
_queryBindGroup;
|
|
96
|
+
_queryPoints;
|
|
97
|
+
_queryOutput;
|
|
98
|
+
_queryChain;
|
|
99
|
+
_levelInfo;
|
|
100
|
+
_dynamicData;
|
|
101
|
+
_dynamicInfo;
|
|
102
|
+
_waterline;
|
|
103
|
+
_contentVersion;
|
|
104
|
+
_staticVersion;
|
|
105
|
+
_prevDynamic;
|
|
106
|
+
_params;
|
|
107
|
+
constructor(sdf, options){
|
|
108
|
+
this._sdf = sdf;
|
|
109
|
+
this.levels = Math.max(1, Math.min(8, Math.floor(options?.levels ?? 4)));
|
|
110
|
+
this.resolution = Math.max(BRICK * 2, Math.ceil((options?.resolution ?? 128) / BRICK) * BRICK);
|
|
111
|
+
this.baseVoxelSize = Math.max(1e-3, options?.baseVoxelSize ?? 0.25);
|
|
112
|
+
this.bandVoxels = Math.max(1, options?.bandVoxels ?? 4);
|
|
113
|
+
this.brickBudget = Math.max(1, Math.floor(options?.brickBudget ?? 512));
|
|
114
|
+
this._texture = null;
|
|
115
|
+
this._tags = null;
|
|
116
|
+
this._origins = new Array(this.levels).fill(null);
|
|
117
|
+
this._dirty = Array.from({
|
|
118
|
+
length: this.levels
|
|
119
|
+
}, ()=>new Set());
|
|
120
|
+
this._lastBricks = 0;
|
|
121
|
+
this._freshBricks = new Float32Array(this.brickBudget * 4);
|
|
122
|
+
this._freshCount = 0;
|
|
123
|
+
this._totalBricks = 0;
|
|
124
|
+
this._terrain = null;
|
|
125
|
+
this._terrainVersion = -1;
|
|
126
|
+
this._program = null;
|
|
127
|
+
this._bindGroup = null;
|
|
128
|
+
this._instanceBuffer = null;
|
|
129
|
+
this._brickBuffer = null;
|
|
130
|
+
this._indexBuffer = null;
|
|
131
|
+
this._materialBuffer = null;
|
|
132
|
+
this._materials = null;
|
|
133
|
+
this._dummyHeightMap = null;
|
|
134
|
+
this._queryProgram = null;
|
|
135
|
+
this._queryBindGroup = null;
|
|
136
|
+
this._queryPoints = null;
|
|
137
|
+
this._queryOutput = null;
|
|
138
|
+
this._queryChain = Promise.resolve();
|
|
139
|
+
this._levelInfo = new Float32Array(this.levels * 4);
|
|
140
|
+
this._dynamicData = new Float32Array(MAX_DYNAMIC_SDF_INSTANCES * SDF_INSTANCE_VEC4S * 4);
|
|
141
|
+
this._dynamicInfo = new Vector4();
|
|
142
|
+
this._waterline = null;
|
|
143
|
+
this._contentVersion = 0;
|
|
144
|
+
this._staticVersion = 0;
|
|
145
|
+
this._prevDynamic = new Float32Array(this._dynamicData.length + 1);
|
|
146
|
+
this._params = new Vector4(this.resolution, this.levels, this.bandVoxels, 0);
|
|
147
|
+
}
|
|
148
|
+
/** True on a device that can build the field. */ static isSupported() {
|
|
149
|
+
return getDevice().type === 'webgpu';
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The levels stacked along z in {@link globalSDFFormat}, `resolution + 1` slices each; the last
|
|
153
|
+
* copies the first so filtering wraps across the toroidal seam in z as repeat does in x and y.
|
|
154
|
+
*/ get texture() {
|
|
155
|
+
if (!this._texture) {
|
|
156
|
+
const n = this.resolution;
|
|
157
|
+
this._texture = getDevice().createTexture3D(globalSDFFormat(), n, n, (n + 1) * this.levels, {
|
|
158
|
+
mipmapping: false,
|
|
159
|
+
writable: true
|
|
160
|
+
});
|
|
161
|
+
this._texture.name = '@GlobalSDF';
|
|
162
|
+
}
|
|
163
|
+
return this._texture;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* One texel per brick, levels stacked along z: (world brick x, y, z, 1) of
|
|
167
|
+
* the brick last composited there. Starts all zero, which no brick matches.
|
|
168
|
+
*/ get tags() {
|
|
169
|
+
if (!this._tags) {
|
|
170
|
+
const nb = this.resolution / BRICK;
|
|
171
|
+
this._tags = getDevice().createTexture3D('rgba32f', nb, nb, nb * this.levels, {
|
|
172
|
+
mipmapping: false,
|
|
173
|
+
writable: true
|
|
174
|
+
});
|
|
175
|
+
this._tags.name = '@GlobalSDFTags';
|
|
176
|
+
}
|
|
177
|
+
return this._tags;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Bricks whose materials the last update composited, for immediate voxel lighting: per brick
|
|
181
|
+
* (first material voxel within its level, level), `count` of them.
|
|
182
|
+
*/ get freshMaterialBricks() {
|
|
183
|
+
return {
|
|
184
|
+
data: this._freshBricks,
|
|
185
|
+
count: this._freshCount
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
/** Voxel size of a level in world units. */ voxelSize(level) {
|
|
189
|
+
return this.baseVoxelSize * 2 ** level;
|
|
190
|
+
}
|
|
191
|
+
/** (window origin in voxels, voxel size) per level, the `levels` uniform of {@link sampleGlobalSDF}. */ get levelInfo() {
|
|
192
|
+
return this._levelInfo;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* (resolution, levels, band voxels, current mask), the `params` uniform of
|
|
196
|
+
* {@link sampleGlobalSDF}; bit i of the mask set when every brick of level i
|
|
197
|
+
* is composited for its window, so readers can skip the tag checks there.
|
|
198
|
+
*/ get params() {
|
|
199
|
+
return this._params;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Horizontal distance to the waterline at a water level, for shoreline
|
|
203
|
+
* effects; built on demand, see {@link WaterlineField.request}.
|
|
204
|
+
*/ get waterline() {
|
|
205
|
+
if (!this._waterline) {
|
|
206
|
+
this._waterline = new WaterlineField(new SDFWaterlineSource(this));
|
|
207
|
+
}
|
|
208
|
+
return this._waterline;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Increments whenever the field may have changed (dirty region, bricks composited, dynamic
|
|
212
|
+
* instances moved), so derived data such as the waterline can skip rebuilding.
|
|
213
|
+
*/ get contentVersion() {
|
|
214
|
+
return this._contentVersion;
|
|
215
|
+
}
|
|
216
|
+
/** As {@link GlobalSDF.contentVersion}, but for the clipmap only (static geometry and terrain). */ get staticVersion() {
|
|
217
|
+
return this._staticVersion;
|
|
218
|
+
}
|
|
219
|
+
/** Dynamic instances merged at query time this frame. */ get dynamicCount() {
|
|
220
|
+
return this._dynamicInfo.x;
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* The materials composited alongside the distances, or null for distances
|
|
224
|
+
* alone. Changing it rebuilds the whole field.
|
|
225
|
+
*/ get materials() {
|
|
226
|
+
return this._materials;
|
|
227
|
+
}
|
|
228
|
+
set materials(val) {
|
|
229
|
+
if (val !== this._materials) {
|
|
230
|
+
this._materials = val;
|
|
231
|
+
this._program?.dispose();
|
|
232
|
+
this._program = null;
|
|
233
|
+
this._bindGroup?.dispose();
|
|
234
|
+
this._bindGroup = null;
|
|
235
|
+
this.invalidate();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Binds the field for {@link sampleSceneSDF} in a program that declared the
|
|
240
|
+
* global and the dynamic uniforms.
|
|
241
|
+
*/ applyBindGroup(bindGroup) {
|
|
242
|
+
const atlas = this._sdf.atlas;
|
|
243
|
+
bindGroup.setTexture(SDF_ATLAS_TEXTURE, atlas.texture, fetchSampler('clamp_linear_nomip'));
|
|
244
|
+
bindGroup.setValue(SDF_DYNAMIC_INSTANCES, this._dynamicData);
|
|
245
|
+
bindGroup.setValue(SDF_DYNAMIC_INFO, this._dynamicInfo);
|
|
246
|
+
// Read with textureLoad, but the unfilterable binding still has a sampler
|
|
247
|
+
// slot, which a filtering sampler fails to validate against
|
|
248
|
+
bindGroup.setTexture(GLOBAL_SDF_TEXTURE, this.texture, fetchSampler(isGlobalSDFFilterable() ? 'repeat_linear_nomip' : 'clamp_nearest_nomip'));
|
|
249
|
+
bindGroup.setTexture(GLOBAL_SDF_TAGS, this.tags, fetchSampler('clamp_nearest_nomip'));
|
|
250
|
+
bindGroup.setValue(GLOBAL_SDF_LEVELS, this._levelInfo);
|
|
251
|
+
bindGroup.setValue(GLOBAL_SDF_PARAMS, this._params);
|
|
252
|
+
}
|
|
253
|
+
getStats() {
|
|
254
|
+
let dirty = 0;
|
|
255
|
+
for (const set of this._dirty){
|
|
256
|
+
dirty += set.size;
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
levels: this.levels,
|
|
260
|
+
resolution: this.resolution,
|
|
261
|
+
voxelSizes: Array.from({
|
|
262
|
+
length: this.levels
|
|
263
|
+
}, (_, i)=>this.voxelSize(i)),
|
|
264
|
+
origins: this._origins.map((o, i)=>o ? o.map((v)=>v * this.voxelSize(i)) : [
|
|
265
|
+
0,
|
|
266
|
+
0,
|
|
267
|
+
0
|
|
268
|
+
]),
|
|
269
|
+
dirtyBricks: dirty,
|
|
270
|
+
lastBricks: this._lastBricks,
|
|
271
|
+
totalBricks: this._totalBricks,
|
|
272
|
+
terrain: !!this._terrain,
|
|
273
|
+
waterlineRebuilds: this._waterline?.rebuilds ?? 0
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
/** Shader compile errors of the composite and query programs, for diagnosis. */ getProgramErrors() {
|
|
277
|
+
return getSDFProgramErrors();
|
|
278
|
+
}
|
|
279
|
+
/** Marks every brick within band reach of a world box for rebuilding. */ markDirty(min, max) {
|
|
280
|
+
this._contentVersion++;
|
|
281
|
+
this._staticVersion++;
|
|
282
|
+
for(let level = 0; level < this.levels; level++){
|
|
283
|
+
const origin = this._origins[level];
|
|
284
|
+
if (!origin) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
const vs = this.voxelSize(level);
|
|
288
|
+
const reach = this.bandVoxels * vs;
|
|
289
|
+
const b = BRICK * vs;
|
|
290
|
+
const lo = [
|
|
291
|
+
min.x - reach,
|
|
292
|
+
min.y - reach,
|
|
293
|
+
min.z - reach
|
|
294
|
+
].map((v)=>Math.floor(v / b));
|
|
295
|
+
const hi = [
|
|
296
|
+
max.x + reach,
|
|
297
|
+
max.y + reach,
|
|
298
|
+
max.z + reach
|
|
299
|
+
].map((v)=>Math.floor(v / b));
|
|
300
|
+
const wlo = origin.map((v)=>v / BRICK);
|
|
301
|
+
const whi = wlo.map((v)=>v + this.resolution / BRICK - 1);
|
|
302
|
+
for(let z = Math.max(lo[2], wlo[2]); z <= Math.min(hi[2], whi[2]); z++){
|
|
303
|
+
for(let y = Math.max(lo[1], wlo[1]); y <= Math.min(hi[1], whi[1]); y++){
|
|
304
|
+
for(let x = Math.max(lo[0], wlo[0]); x <= Math.min(hi[0], whi[0]); x++){
|
|
305
|
+
this._dirty[level].add(`${x},${y},${z}`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/** Marks everything for rebuilding. */ invalidate() {
|
|
312
|
+
for(let level = 0; level < this.levels; level++){
|
|
313
|
+
this._origins[level] = null;
|
|
314
|
+
this._dirty[level].clear();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Follows the camera and composites up to {@link GlobalSDF.brickBudget} dirty
|
|
319
|
+
* bricks. Called by the scene field once a frame, after its atlas uploads.
|
|
320
|
+
*/ update(cameraPos, terrain) {
|
|
321
|
+
if (terrain !== this._terrain || terrain && terrain.heightVersion !== this._terrainVersion) {
|
|
322
|
+
const old = this._terrain;
|
|
323
|
+
this._terrain = terrain;
|
|
324
|
+
this._terrainVersion = terrain?.heightVersion ?? -1;
|
|
325
|
+
for (const t of [
|
|
326
|
+
old,
|
|
327
|
+
terrain
|
|
328
|
+
]){
|
|
329
|
+
const box = t?.getWorldBoundingVolume()?.toAABB();
|
|
330
|
+
if (box) {
|
|
331
|
+
this.markDirty(box.minPoint, box.maxPoint);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (this._materials && terrain) {
|
|
336
|
+
// Recapture the terrain albedo where it changed and recomposite the materials there
|
|
337
|
+
const rect = terrain.giAlbedo.update();
|
|
338
|
+
const box = rect ? terrain.getWorldBoundingVolume()?.toAABB() : null;
|
|
339
|
+
if (rect && box) {
|
|
340
|
+
this.markDirty(tmpDirtyMin.setXYZ(rect[0], box.minPoint.y, rect[1]), tmpDirtyMax.setXYZ(rect[2], box.maxPoint.y, rect[3]));
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
for(let level = 0; level < this.levels; level++){
|
|
344
|
+
this.moveWindow(level, cameraPos);
|
|
345
|
+
}
|
|
346
|
+
this.composite();
|
|
347
|
+
// Levels with nothing left to composite hold their whole window
|
|
348
|
+
let current = 0;
|
|
349
|
+
for(let level = 0; level < this.levels; level++){
|
|
350
|
+
if (this._origins[level] && this._dirty[level].size === 0) {
|
|
351
|
+
current |= 1 << level;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
this._params.w = current;
|
|
355
|
+
this.gatherDynamic(cameraPos);
|
|
356
|
+
if (this._waterline) {
|
|
357
|
+
RenderGraphExecutor.profileScope('Waterline', ()=>this._waterline.update(cameraPos));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/** Packs the ready dynamic instances nearest the camera for query-time merging. @internal */ gatherDynamic(cameraPos) {
|
|
361
|
+
const reach = this.resolution * 0.5 * this.voxelSize(this.levels - 1);
|
|
362
|
+
const near = [];
|
|
363
|
+
for (const inst of this._sdf.instances){
|
|
364
|
+
if (inst.mode === 'dynamic' && this._sdf.isReady(inst)) {
|
|
365
|
+
const lo = inst.bounds.minPoint;
|
|
366
|
+
const hi = inst.bounds.maxPoint;
|
|
367
|
+
const d = Math.hypot(Math.max(lo.x - cameraPos.x, 0, cameraPos.x - hi.x), Math.max(lo.y - cameraPos.y, 0, cameraPos.y - hi.y), Math.max(lo.z - cameraPos.z, 0, cameraPos.z - hi.z));
|
|
368
|
+
if (d <= reach) {
|
|
369
|
+
near.push({
|
|
370
|
+
inst,
|
|
371
|
+
d
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
near.sort((a, b)=>a.d - b.d);
|
|
377
|
+
const count = Math.min(near.length, MAX_DYNAMIC_SDF_INSTANCES);
|
|
378
|
+
const atlas = this._sdf.atlas;
|
|
379
|
+
for(let i = 0; i < count; i++){
|
|
380
|
+
packSDFInstance(near[i].inst, atlas, this._dynamicData, i * SDF_INSTANCE_VEC4S * 4);
|
|
381
|
+
}
|
|
382
|
+
this._dynamicInfo.setXYZW(count, 1 / atlas.size[0], 1 / atlas.size[1], 1 / atlas.size[2]);
|
|
383
|
+
// Changed when an instance moved or the set changed
|
|
384
|
+
const used = count * SDF_INSTANCE_VEC4S * 4;
|
|
385
|
+
let changed = this._prevDynamic[0] !== count;
|
|
386
|
+
for(let i = 0; i < used && !changed; i++){
|
|
387
|
+
changed = this._prevDynamic[i + 1] !== this._dynamicData[i];
|
|
388
|
+
}
|
|
389
|
+
if (changed) {
|
|
390
|
+
this._prevDynamic[0] = count;
|
|
391
|
+
this._prevDynamic.set(this._dynamicData.subarray(0, used), 1);
|
|
392
|
+
this._contentVersion++;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Signed distances at world positions, read back from the GPU; resolves a
|
|
397
|
+
* frame or two later. For tools and gameplay queries, not per-frame use.
|
|
398
|
+
*/ queryDistances(points) {
|
|
399
|
+
const run = async ()=>{
|
|
400
|
+
const n = Math.min(points.length, MAX_QUERY_POINTS);
|
|
401
|
+
const out = new Float32Array(points.length);
|
|
402
|
+
if (n === 0 || !this._texture) {
|
|
403
|
+
return out;
|
|
404
|
+
}
|
|
405
|
+
const device = getDevice();
|
|
406
|
+
const pixels = new Float32Array(MAX_QUERY_POINTS);
|
|
407
|
+
// Recorded at the start of a frame, like the water surface queries:
|
|
408
|
+
// outside one the dispatch has no command stream to land in
|
|
409
|
+
await device.runNextFrameAsync(async ()=>{
|
|
410
|
+
if (!this._queryProgram) {
|
|
411
|
+
this._queryProgram = this.createQueryProgram();
|
|
412
|
+
this._queryBindGroup = device.createBindGroup(this._queryProgram.bindGroupLayouts[0]);
|
|
413
|
+
this._queryPoints = device.createBuffer(MAX_QUERY_POINTS * 16, {
|
|
414
|
+
usage: 'uniform',
|
|
415
|
+
storage: true
|
|
416
|
+
});
|
|
417
|
+
this._queryOutput = device.createTexture2D('r32f', MAX_QUERY_POINTS, 1, {
|
|
418
|
+
mipmapping: false,
|
|
419
|
+
writable: true
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
const data = new Float32Array(MAX_QUERY_POINTS * 4);
|
|
423
|
+
for(let i = 0; i < n; i++){
|
|
424
|
+
data[i * 4] = points[i].x;
|
|
425
|
+
data[i * 4 + 1] = points[i].y;
|
|
426
|
+
data[i * 4 + 2] = points[i].z;
|
|
427
|
+
}
|
|
428
|
+
this._queryPoints.bufferSubData(0, data);
|
|
429
|
+
const bg = this._queryBindGroup;
|
|
430
|
+
bg.setBuffer('points', this._queryPoints);
|
|
431
|
+
bg.setTexture('result', this._queryOutput);
|
|
432
|
+
bg.setValue('count', new Vector4(n, 0, 0, 0));
|
|
433
|
+
this.applyBindGroup(bg);
|
|
434
|
+
device.setProgram(this._queryProgram);
|
|
435
|
+
device.setBindGroup(0, bg);
|
|
436
|
+
device.compute(Math.ceil(n / 64), 1, 1);
|
|
437
|
+
// The copy for the readback is recorded right after the dispatch, and
|
|
438
|
+
// flushed so it does not wait behind the frame's own passes
|
|
439
|
+
const read = this._queryOutput.readPixels(0, 0, MAX_QUERY_POINTS, 1, 0, 0, pixels);
|
|
440
|
+
device.flush();
|
|
441
|
+
await read;
|
|
442
|
+
});
|
|
443
|
+
out.set(pixels.subarray(0, n));
|
|
444
|
+
return out;
|
|
445
|
+
};
|
|
446
|
+
const next = this._queryChain.then(run, run);
|
|
447
|
+
this._queryChain = next.catch(()=>undefined);
|
|
448
|
+
return next;
|
|
449
|
+
}
|
|
450
|
+
dispose() {
|
|
451
|
+
this._waterline?.dispose();
|
|
452
|
+
this._waterline = null;
|
|
453
|
+
this._texture?.dispose();
|
|
454
|
+
this._texture = null;
|
|
455
|
+
this._tags?.dispose();
|
|
456
|
+
this._tags = null;
|
|
457
|
+
this._instanceBuffer?.dispose();
|
|
458
|
+
this._brickBuffer?.dispose();
|
|
459
|
+
this._indexBuffer?.dispose();
|
|
460
|
+
this._materialBuffer?.dispose();
|
|
461
|
+
this._dummyHeightMap?.dispose();
|
|
462
|
+
this._queryPoints?.dispose();
|
|
463
|
+
this._queryOutput?.dispose();
|
|
464
|
+
this._bindGroup?.dispose();
|
|
465
|
+
this._queryBindGroup?.dispose();
|
|
466
|
+
this._instanceBuffer = this._brickBuffer = this._indexBuffer = this._queryPoints = null;
|
|
467
|
+
this._materialBuffer = null;
|
|
468
|
+
this._dummyHeightMap = this._queryOutput = null;
|
|
469
|
+
this._bindGroup = this._queryBindGroup = null;
|
|
470
|
+
this.invalidate();
|
|
471
|
+
}
|
|
472
|
+
/** Re-centres a level on the camera, dirtying the bricks that entered. @internal */ moveWindow(level, cameraPos) {
|
|
473
|
+
const vs = this.voxelSize(level);
|
|
474
|
+
const n = this.resolution;
|
|
475
|
+
const nb = n / BRICK;
|
|
476
|
+
// Snapped to bricks so a brick always maps to one contiguous block of texels
|
|
477
|
+
const next = [
|
|
478
|
+
cameraPos.x,
|
|
479
|
+
cameraPos.y,
|
|
480
|
+
cameraPos.z
|
|
481
|
+
].map((c)=>(Math.floor(c / (vs * BRICK)) - nb / 2) * BRICK);
|
|
482
|
+
const prev = this._origins[level];
|
|
483
|
+
if (prev && prev[0] === next[0] && prev[1] === next[1] && prev[2] === next[2]) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
this._origins[level] = next;
|
|
487
|
+
this._levelInfo.set([
|
|
488
|
+
next[0],
|
|
489
|
+
next[1],
|
|
490
|
+
next[2],
|
|
491
|
+
vs
|
|
492
|
+
], level * 4);
|
|
493
|
+
const lo = next.map((v)=>v / BRICK);
|
|
494
|
+
const plo = prev?.map((v)=>v / BRICK);
|
|
495
|
+
const dirty = this._dirty[level];
|
|
496
|
+
// Drop dirty bricks that left the window
|
|
497
|
+
for (const key of dirty){
|
|
498
|
+
const [x, y, z] = key.split(',').map(Number);
|
|
499
|
+
if (x < lo[0] || y < lo[1] || z < lo[2] || x >= lo[0] + nb || y >= lo[1] + nb || z >= lo[2] + nb) {
|
|
500
|
+
dirty.delete(key);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
for(let z = lo[2]; z < lo[2] + nb; z++){
|
|
504
|
+
for(let y = lo[1]; y < lo[1] + nb; y++){
|
|
505
|
+
for(let x = lo[0]; x < lo[0] + nb; x++){
|
|
506
|
+
const inPrev = plo && x >= plo[0] && y >= plo[1] && z >= plo[2] && x < plo[0] + nb && y < plo[1] + nb && z < plo[2] + nb;
|
|
507
|
+
if (!inPrev) {
|
|
508
|
+
dirty.add(`${x},${y},${z}`);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
/** Composites a budget of dirty bricks in one dispatch. @internal */ composite() {
|
|
515
|
+
// Coarse levels first: a coarse answer everywhere beats a fine one somewhere
|
|
516
|
+
const batch = [];
|
|
517
|
+
for(let level = this.levels - 1; level >= 0 && batch.length < this.brickBudget; level--){
|
|
518
|
+
const dirty = this._dirty[level];
|
|
519
|
+
for (const key of dirty){
|
|
520
|
+
if (batch.length >= this.brickBudget) {
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
const [x, y, z] = key.split(',').map(Number);
|
|
524
|
+
batch.push({
|
|
525
|
+
level,
|
|
526
|
+
x,
|
|
527
|
+
y,
|
|
528
|
+
z
|
|
529
|
+
});
|
|
530
|
+
dirty.delete(key);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
this._lastBricks = batch.length;
|
|
534
|
+
this._freshCount = 0;
|
|
535
|
+
// The budget can be raised at run time
|
|
536
|
+
if (this._freshBricks.length < batch.length * 4) {
|
|
537
|
+
this._freshBricks = new Float32Array(batch.length * 4);
|
|
538
|
+
}
|
|
539
|
+
if (batch.length === 0) {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
this._contentVersion++;
|
|
543
|
+
this._staticVersion++;
|
|
544
|
+
this._totalBricks += batch.length;
|
|
545
|
+
// Static instances whose field is on the GPU
|
|
546
|
+
const instances = [];
|
|
547
|
+
for (const inst of this._sdf.instances){
|
|
548
|
+
if (inst.mode === 'static' && this._sdf.isReady(inst)) {
|
|
549
|
+
instances.push(inst);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const atlas = this._sdf.atlas;
|
|
553
|
+
const instData = new Float32Array(Math.max(1, instances.length) * SDF_INSTANCE_VEC4S * 4);
|
|
554
|
+
instances.forEach((inst, i)=>packSDFInstance(inst, atlas, instData, i * SDF_INSTANCE_VEC4S * 4));
|
|
555
|
+
// Per brick: the instances whose bounds come within band reach of it
|
|
556
|
+
const brickData = new Float32Array(batch.length * BRICK_VEC4S * 4);
|
|
557
|
+
const indices = [];
|
|
558
|
+
const terrainBox = this._terrain?.getWorldBoundingVolume()?.toAABB() ?? null;
|
|
559
|
+
const region = this._terrain?.worldRegion ?? null;
|
|
560
|
+
const n = this.resolution;
|
|
561
|
+
batch.forEach((brick, b)=>{
|
|
562
|
+
const vs = this.voxelSize(brick.level);
|
|
563
|
+
const reach = this.bandVoxels * vs;
|
|
564
|
+
const size = BRICK * vs;
|
|
565
|
+
const min = [
|
|
566
|
+
brick.x * size - reach,
|
|
567
|
+
brick.y * size - reach,
|
|
568
|
+
brick.z * size - reach
|
|
569
|
+
];
|
|
570
|
+
const max = [
|
|
571
|
+
min[0] + size + 2 * reach,
|
|
572
|
+
min[1] + size + 2 * reach,
|
|
573
|
+
min[2] + size + 2 * reach
|
|
574
|
+
];
|
|
575
|
+
const start = indices.length;
|
|
576
|
+
// Whether a two-sided mesh comes near, which narrows the trace's surface
|
|
577
|
+
// expand in the brick (see SDF_NOT_COVERED_EXPAND_SCALE)
|
|
578
|
+
let twoSided = false;
|
|
579
|
+
instances.forEach((inst, i)=>{
|
|
580
|
+
const lo = inst.bounds.minPoint;
|
|
581
|
+
const hi = inst.bounds.maxPoint;
|
|
582
|
+
if (lo.x <= max[0] && hi.x >= min[0] && lo.y <= max[1] && hi.y >= min[1] && lo.z <= max[2] && hi.z >= min[2]) {
|
|
583
|
+
indices.push(i);
|
|
584
|
+
twoSided ||= !!inst.asset.header.twoSided;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
587
|
+
const terrainHit = !!terrainBox && !!region && terrainBox.minPoint.y <= max[1] && terrainBox.maxPoint.y >= min[1] && region.x <= max[0] && region.z >= min[0] && region.y <= max[2] && region.w >= min[2];
|
|
588
|
+
const wrap = (v)=>(v * BRICK % n + n) % n;
|
|
589
|
+
const o = b * BRICK_VEC4S * 4;
|
|
590
|
+
brickData.set([
|
|
591
|
+
brick.x * BRICK,
|
|
592
|
+
brick.y * BRICK,
|
|
593
|
+
brick.z * BRICK,
|
|
594
|
+
vs
|
|
595
|
+
], o);
|
|
596
|
+
if (this._materials) {
|
|
597
|
+
// Material voxels are 2³ field voxels
|
|
598
|
+
this._freshBricks.set([
|
|
599
|
+
wrap(brick.x) / 2,
|
|
600
|
+
wrap(brick.y) / 2,
|
|
601
|
+
wrap(brick.z) / 2,
|
|
602
|
+
brick.level
|
|
603
|
+
], this._freshCount++ * 4);
|
|
604
|
+
}
|
|
605
|
+
brickData.set([
|
|
606
|
+
wrap(brick.x),
|
|
607
|
+
wrap(brick.y),
|
|
608
|
+
wrap(brick.z) + brick.level * n,
|
|
609
|
+
start
|
|
610
|
+
], o + 4);
|
|
611
|
+
// y: bit 0 the terrain comes near, bit 1 a two-sided mesh does
|
|
612
|
+
brickData.set([
|
|
613
|
+
indices.length - start,
|
|
614
|
+
(terrainHit ? 1 : 0) + (twoSided ? 2 : 0),
|
|
615
|
+
reach,
|
|
616
|
+
brick.level
|
|
617
|
+
], o + 8);
|
|
618
|
+
});
|
|
619
|
+
const device = getDevice();
|
|
620
|
+
const program = this.getProgram();
|
|
621
|
+
const bg = this._bindGroup;
|
|
622
|
+
this._instanceBuffer = this.ensureBuffer(this._instanceBuffer, instData.byteLength);
|
|
623
|
+
this._instanceBuffer.bufferSubData(0, instData);
|
|
624
|
+
this._brickBuffer = this.ensureBuffer(this._brickBuffer, brickData.byteLength);
|
|
625
|
+
this._brickBuffer.bufferSubData(0, brickData);
|
|
626
|
+
const indexData = new Uint32Array(Math.max(4, indices.length));
|
|
627
|
+
indexData.set(indices);
|
|
628
|
+
this._indexBuffer = this.ensureBuffer(this._indexBuffer, indexData.byteLength);
|
|
629
|
+
this._indexBuffer.bufferSubData(0, indexData);
|
|
630
|
+
bg.setBuffer('instances', this._instanceBuffer);
|
|
631
|
+
bg.setBuffer('bricks', this._brickBuffer);
|
|
632
|
+
bg.setBuffer('indices', this._indexBuffer);
|
|
633
|
+
const materials = this._materials;
|
|
634
|
+
if (materials) {
|
|
635
|
+
// Per instance: (atlas slot offset, 1 when resident), (slot resolution, 0)
|
|
636
|
+
const matData = new Float32Array(Math.max(1, instances.length) * 8);
|
|
637
|
+
instances.forEach((inst, i)=>{
|
|
638
|
+
const slot = materials.getSlot(inst);
|
|
639
|
+
if (slot) {
|
|
640
|
+
matData.set([
|
|
641
|
+
slot.offset[0],
|
|
642
|
+
slot.offset[1],
|
|
643
|
+
slot.offset[2],
|
|
644
|
+
1
|
|
645
|
+
], i * 8);
|
|
646
|
+
matData.set([
|
|
647
|
+
slot.resolution[0],
|
|
648
|
+
slot.resolution[1],
|
|
649
|
+
slot.resolution[2],
|
|
650
|
+
0
|
|
651
|
+
], i * 8 + 4);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
this._materialBuffer = this.ensureBuffer(this._materialBuffer, matData.byteLength);
|
|
655
|
+
this._materialBuffer.bufferSubData(0, matData);
|
|
656
|
+
bg.setBuffer('materialSlots', this._materialBuffer);
|
|
657
|
+
bg.setTexture('matAlbedoAtlas', materials.atlasAlbedo, fetchSampler('clamp_linear_nomip'));
|
|
658
|
+
// RGBE must not be filtered
|
|
659
|
+
bg.setTexture('matEmissiveAtlas', materials.atlasEmissive, fetchSampler('clamp_nearest_nomip'));
|
|
660
|
+
const s = materials.atlasSize;
|
|
661
|
+
bg.setValue('matAtlasInvSize', new Vector4(1 / s[0], 1 / s[1], 1 / s[2], 0));
|
|
662
|
+
const terrainAlbedo = this._terrain?.giAlbedo.texture ?? null;
|
|
663
|
+
bg.setTexture('terrainAlbedo', terrainAlbedo ?? getDefaultTexture2D(), fetchSampler('clamp_linear'));
|
|
664
|
+
bg.setValue('terrainAlbedoSize', new Vector4(terrainAlbedo?.width ?? 1, terrainAlbedo?.height ?? 1, 0, 0));
|
|
665
|
+
bg.setTexture('albedoOut', materials.albedo);
|
|
666
|
+
bg.setTexture('emissiveOut', materials.emissive);
|
|
667
|
+
}
|
|
668
|
+
bg.setTexture('atlas', atlas.texture, fetchSampler('clamp_linear_nomip'));
|
|
669
|
+
bg.setValue('atlasInvSize', new Vector4(1 / atlas.size[0], 1 / atlas.size[1], 1 / atlas.size[2], 0));
|
|
670
|
+
const terrain = this._terrain;
|
|
671
|
+
const heightMap = terrain?.heightMap ?? null;
|
|
672
|
+
bg.setTexture('heightMap', heightMap ?? this.getDummyHeightMap(), fetchSampler('clamp_linear_nomip'));
|
|
673
|
+
if (terrain && heightMap && region) {
|
|
674
|
+
bg.setValue('terrainRegion', new Vector4(region.x, region.y, region.z, region.w));
|
|
675
|
+
bg.setValue('terrainParams', new Vector4(Math.abs(terrain.scale.y), terrain.worldMatrix.m13, 1 / heightMap.width, 1 / heightMap.height));
|
|
676
|
+
} else {
|
|
677
|
+
bg.setValue('terrainRegion', new Vector4(0, 0, 1, 1));
|
|
678
|
+
bg.setValue('terrainParams', new Vector4(0, 0, 1, 1));
|
|
679
|
+
}
|
|
680
|
+
bg.setTexture('field', this.texture);
|
|
681
|
+
bg.setTexture('tagsOut', this.tags);
|
|
682
|
+
device.setProgram(program);
|
|
683
|
+
device.setBindGroup(0, bg);
|
|
684
|
+
device.compute(batch.length * 8, 1, 1);
|
|
685
|
+
}
|
|
686
|
+
/** @internal */ ensureBuffer(buffer, bytes) {
|
|
687
|
+
if (buffer && buffer.byteLength >= bytes) {
|
|
688
|
+
return buffer;
|
|
689
|
+
}
|
|
690
|
+
buffer?.dispose();
|
|
691
|
+
// Grow by 1.5x to avoid per-frame reallocation; whole vec4s since the storage binding covers
|
|
692
|
+
// the whole buffer
|
|
693
|
+
return getDevice().createBuffer(Math.max(256, Math.ceil(bytes * 1.5 / 16) * 16), {
|
|
694
|
+
usage: 'uniform',
|
|
695
|
+
storage: true
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
/** @internal */ getDummyHeightMap() {
|
|
699
|
+
if (!this._dummyHeightMap) {
|
|
700
|
+
this._dummyHeightMap = getDevice().createTexture2D('r16f', 1, 1, {
|
|
701
|
+
mipmapping: false
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
return this._dummyHeightMap;
|
|
705
|
+
}
|
|
706
|
+
/** @internal */ getProgram() {
|
|
707
|
+
if (!this._program) {
|
|
708
|
+
const device = getDevice();
|
|
709
|
+
const materials = !!this._materials;
|
|
710
|
+
const fieldRes = this.resolution;
|
|
711
|
+
this._program = buildSDFProgram({
|
|
712
|
+
label: materials ? 'GlobalSDFCompositeMaterials' : 'GlobalSDFComposite',
|
|
713
|
+
workgroupSize: [
|
|
714
|
+
4,
|
|
715
|
+
4,
|
|
716
|
+
4
|
|
717
|
+
],
|
|
718
|
+
compute (pb) {
|
|
719
|
+
this.instances = pb.vec4[0]().storageBufferReadonly(0);
|
|
720
|
+
this.bricks = pb.vec4[0]().storageBufferReadonly(0);
|
|
721
|
+
this.indices = pb.uint[0]().storageBufferReadonly(0);
|
|
722
|
+
this.atlas = pb.tex3D().uniform(0);
|
|
723
|
+
this.atlasInvSize = pb.vec4().uniform(0);
|
|
724
|
+
this.heightMap = pb.tex2D().uniform(0);
|
|
725
|
+
this.terrainRegion = pb.vec4().uniform(0);
|
|
726
|
+
// (height scale, world y, 1 / width, 1 / height)
|
|
727
|
+
this.terrainParams = pb.vec4().uniform(0);
|
|
728
|
+
this.field = globalSDFFormat() === 'r16f' ? pb.texStorage3D.r16float().storage(0) : pb.texStorage3D.r32float().storage(0);
|
|
729
|
+
this.tagsOut = pb.texStorage3D.rgba32float().storage(0);
|
|
730
|
+
if (materials) {
|
|
731
|
+
this.materialSlots = pb.vec4[0]().storageBufferReadonly(0);
|
|
732
|
+
this.matAlbedoAtlas = pb.tex3D().uniform(0);
|
|
733
|
+
this.matEmissiveAtlas = pb.tex3D().uniform(0);
|
|
734
|
+
this.matAtlasInvSize = pb.vec4().uniform(0);
|
|
735
|
+
// Top-down capture of the terrain material albedo, see TerrainGIAlbedo
|
|
736
|
+
this.terrainAlbedo = pb.tex2D().uniform(0);
|
|
737
|
+
// (capture width, capture height, 0, 0)
|
|
738
|
+
this.terrainAlbedoSize = pb.vec4().uniform(0);
|
|
739
|
+
this.albedoOut = pb.texStorage3D.rgba8unorm().storage(0);
|
|
740
|
+
this.emissiveOut = pb.texStorage3D.rgba16float().storage(0);
|
|
741
|
+
}
|
|
742
|
+
pb.func('terrainHeight', [
|
|
743
|
+
pb.vec2('xz')
|
|
744
|
+
], function() {
|
|
745
|
+
this.$l.uv = pb.div(pb.sub(this.xz, this.terrainRegion.xy), pb.sub(this.terrainRegion.zw, this.terrainRegion.xy));
|
|
746
|
+
this.$return(pb.add(pb.mul(pb.textureSampleLevel(this.heightMap, this.uv, 0).r, this.terrainParams.x), this.terrainParams.y));
|
|
747
|
+
});
|
|
748
|
+
pb.main(function() {
|
|
749
|
+
this.$l.brick = pb.div(this.$builtins.workGroupId.x, 8);
|
|
750
|
+
this.$l.sub = pb.sub(this.$builtins.workGroupId.x, pb.mul(this.brick, 8));
|
|
751
|
+
this.$l.local = pb.add(pb.mul(pb.uvec3(pb.mod(this.sub, 2), pb.mod(pb.div(this.sub, 2), 2), pb.div(this.sub, 4)), 4), this.$builtins.localInvocationId);
|
|
752
|
+
this.$l.b0 = this.bricks.at(pb.mul(this.brick, BRICK_VEC4S));
|
|
753
|
+
this.$l.b1 = this.bricks.at(pb.add(pb.mul(this.brick, BRICK_VEC4S), 1));
|
|
754
|
+
this.$l.b2 = this.bricks.at(pb.add(pb.mul(this.brick, BRICK_VEC4S), 2));
|
|
755
|
+
this.$l.vs = this.b0.w;
|
|
756
|
+
this.$l.band = this.b2.z;
|
|
757
|
+
this.$l.p = pb.mul(pb.add(this.b0.xyz, pb.add(pb.vec3(this.local), pb.vec3(0.5))), this.vs);
|
|
758
|
+
this.$l.d = this.band;
|
|
759
|
+
this.$l.start = pb.uint(this.b1.w);
|
|
760
|
+
this.$l.count = pb.uint(this.b2.x);
|
|
761
|
+
this.d = sampleInstancesSDF(this, this.p, this.count, this.d, (scope, k)=>pb.mul(scope.indices.at(pb.add(scope.start, k)), SDF_INSTANCE_VEC4S), (scope, i)=>scope.instances.at(i), this.atlas, this.atlasInvSize.xyz, pb.mul(this.vs, 0.5));
|
|
762
|
+
this.$l.flags = pb.uint(this.b2.y);
|
|
763
|
+
this.$l.terrainD = this.band;
|
|
764
|
+
this.$if(pb.notEqual(pb.compAnd(this.flags, pb.uint(1)), pb.uint(0)), function() {
|
|
765
|
+
this.$l.inRegion = pb.and(pb.all(pb.greaterThanEqual(this.p.xz, this.terrainRegion.xy)), pb.all(pb.lessThanEqual(this.p.xz, this.terrainRegion.zw)));
|
|
766
|
+
this.$if(this.inRegion, function() {
|
|
767
|
+
this.$l.h = this.terrainHeight(this.p.xz);
|
|
768
|
+
// Central differences one height map texel apart, in world units
|
|
769
|
+
this.$l.cell = pb.mul(pb.sub(this.terrainRegion.zw, this.terrainRegion.xy), this.terrainParams.zw);
|
|
770
|
+
this.$l.hx = pb.sub(this.terrainHeight(pb.add(this.p.xz, pb.vec2(this.cell.x, 0))), this.terrainHeight(pb.sub(this.p.xz, pb.vec2(this.cell.x, 0))));
|
|
771
|
+
this.$l.hz = pb.sub(this.terrainHeight(pb.add(this.p.xz, pb.vec2(0, this.cell.y))), this.terrainHeight(pb.sub(this.p.xz, pb.vec2(0, this.cell.y))));
|
|
772
|
+
this.$l.grad = pb.div(pb.vec2(this.hx, this.hz), pb.mul(this.cell, 2));
|
|
773
|
+
// Height above the surface over the slope: exact on a plane
|
|
774
|
+
this.terrainD = pb.min(this.terrainD, pb.div(pb.sub(this.p.y, this.h), pb.sqrt(pb.add(1, pb.dot(this.grad, this.grad)))));
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
this.d = pb.clamp(pb.min(this.d, this.terrainD), pb.neg(this.band), this.band);
|
|
778
|
+
// b1 addresses the level as `resolution` slices; the field has one more each
|
|
779
|
+
this.$l.level = pb.uint(this.b2.w);
|
|
780
|
+
this.$l.ft = pb.add(pb.uvec3(this.b1.xyz), this.local);
|
|
781
|
+
this.ft.z = pb.add(this.ft.z, this.level);
|
|
782
|
+
pb.textureStore(this.field, this.ft, pb.vec4(this.d, 0, 0, 0));
|
|
783
|
+
// The first slice of the window again past the last, the seam the filter crosses
|
|
784
|
+
this.$l.res = pb.uint(fieldRes);
|
|
785
|
+
this.$if(pb.and(pb.equal(this.local.z, 0), pb.equal(pb.uint(this.b1.z), pb.mul(this.level, this.res))), function() {
|
|
786
|
+
pb.textureStore(this.field, pb.uvec3(this.ft.x, this.ft.y, pb.add(pb.mul(this.level, pb.add(this.res, 1)), this.res)), pb.vec4(this.d, 0, 0, 0));
|
|
787
|
+
});
|
|
788
|
+
// Tag the brick with its world brick coords (visible to readers next pass); alpha
|
|
789
|
+
// flags two-sided meshes
|
|
790
|
+
this.$if(pb.and(pb.equal(this.sub, 0), pb.equal(this.$builtins.localInvocationId, pb.uvec3(0))), function() {
|
|
791
|
+
pb.textureStore(this.tagsOut, pb.div(pb.uvec3(this.b1.xyz), BRICK), pb.vec4(pb.div(this.b0.xyz, BRICK), this.$choice(pb.notEqual(pb.compAnd(this.flags, pb.uint(2)), pb.uint(0)), pb.float(GLOBAL_SDF_TAG_NOT_COVERED), pb.float(1))));
|
|
792
|
+
});
|
|
793
|
+
if (materials) {
|
|
794
|
+
// Material voxels are 2³ field voxels: the even-corner thread writes it, sampled at
|
|
795
|
+
// its centre from the nearest instance (or the terrain where nearer)
|
|
796
|
+
this.$if(pb.equal(pb.mod(this.local, pb.uvec3(2)), pb.uvec3(0)), function() {
|
|
797
|
+
this.$l.pc = pb.add(this.p, pb.vec3(pb.mul(this.vs, 0.5)));
|
|
798
|
+
this.$l.bestK = pb.int(-1);
|
|
799
|
+
this.$l.instD = sampleInstancesSDF(this, this.pc, this.count, this.band, (scope, k)=>pb.mul(scope.indices.at(pb.add(scope.start, k)), SDF_INSTANCE_VEC4S), (scope, i)=>scope.instances.at(i), this.atlas, this.atlasInvSize.xyz, pb.mul(this.vs, 0.5), 'bestK');
|
|
800
|
+
this.$l.terrainWins = pb.and(pb.lessThan(this.terrainD, this.band), pb.lessThan(this.terrainD, this.instD));
|
|
801
|
+
this.$l.useInst = pb.and(pb.greaterThanEqual(this.bestK, 0), pb.lessThan(this.instD, this.band), pb.not(this.terrainWins));
|
|
802
|
+
this.$l.idx = this.$choice(this.useInst, this.indices.at(pb.add(this.start, pb.uint(pb.max(this.bestK, 0)))), pb.uint(0));
|
|
803
|
+
this.$l.ibase = pb.mul(this.idx, SDF_INSTANCE_VEC4S);
|
|
804
|
+
this.$l.mr0 = this.instances.at(this.ibase);
|
|
805
|
+
this.$l.mr1 = this.instances.at(pb.add(this.ibase, 1));
|
|
806
|
+
this.$l.mr2 = this.instances.at(pb.add(this.ibase, 2));
|
|
807
|
+
this.$l.mbmin = this.instances.at(pb.add(this.ibase, 3));
|
|
808
|
+
this.$l.mbmax = this.instances.at(pb.add(this.ibase, 4));
|
|
809
|
+
this.$l.mslot = this.instances.at(pb.add(this.ibase, 5));
|
|
810
|
+
this.$l.matSlot = this.$choice(this.useInst, this.materialSlots.at(pb.mul(this.idx, 2)), pb.vec4(0));
|
|
811
|
+
this.$l.matRes = this.materialSlots.at(pb.add(pb.mul(this.idx, 2), 1));
|
|
812
|
+
this.$l.pc4 = pb.vec4(this.pc, 1);
|
|
813
|
+
this.$l.lp = pb.vec3(pb.dot(this.mr0, this.pc4), pb.dot(this.mr1, this.pc4), pb.dot(this.mr2, this.pc4));
|
|
814
|
+
sampleInstanceMaterial(this, this.lp, this.mbmin, this.mbmax, this.mslot, this.matSlot, this.matRes, this.atlas, this.atlasInvSize.xyz, this.matAlbedoAtlas, this.matEmissiveAtlas, this.matAtlasInvSize.xyz, 'outAlbedo', 'outEmissive');
|
|
815
|
+
this.$if(this.terrainWins, function() {
|
|
816
|
+
// The capture's mip whose texels match the material voxel (two field voxels),
|
|
817
|
+
// so coarse levels read the average over their footprint
|
|
818
|
+
this.$l.tregion = pb.sub(this.terrainRegion.zw, this.terrainRegion.xy);
|
|
819
|
+
this.$l.tuv = pb.div(pb.sub(this.pc.xz, this.terrainRegion.xy), this.tregion);
|
|
820
|
+
this.$l.ttexel = pb.max(pb.div(this.tregion.x, this.terrainAlbedoSize.x), pb.div(this.tregion.y, this.terrainAlbedoSize.y));
|
|
821
|
+
this.$l.tlod = pb.max(pb.log2(pb.div(pb.mul(this.vs, 2), this.ttexel)), 0);
|
|
822
|
+
this.outAlbedo = pb.vec4(pb.textureSampleLevel(this.terrainAlbedo, this.tuv, this.tlod).rgb, 1);
|
|
823
|
+
this.outEmissive = pb.vec3(0);
|
|
824
|
+
});
|
|
825
|
+
this.$l.ct = pb.div(pb.add(pb.uvec3(this.b1.xyz), this.local), pb.uvec3(2));
|
|
826
|
+
pb.textureStore(this.albedoOut, this.ct, this.outAlbedo);
|
|
827
|
+
pb.textureStore(this.emissiveOut, this.ct, pb.vec4(this.outEmissive, 0));
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
});
|
|
833
|
+
this._bindGroup = device.createBindGroup(this._program.bindGroupLayouts[0]);
|
|
834
|
+
}
|
|
835
|
+
return this._program;
|
|
836
|
+
}
|
|
837
|
+
/** @internal */ createQueryProgram() {
|
|
838
|
+
const levels = this.levels;
|
|
839
|
+
return buildSDFProgram({
|
|
840
|
+
label: 'GlobalSDFQuery',
|
|
841
|
+
workgroupSize: [
|
|
842
|
+
64,
|
|
843
|
+
1,
|
|
844
|
+
1
|
|
845
|
+
],
|
|
846
|
+
compute (pb) {
|
|
847
|
+
this.points = pb.vec4[0]().storageBufferReadonly(0);
|
|
848
|
+
this.count = pb.vec4().uniform(0);
|
|
849
|
+
this.result = pb.texStorage2D.r32float().storage(0);
|
|
850
|
+
declareGlobalSDFUniforms(this, 0, levels);
|
|
851
|
+
declareDynamicSDFUniforms(this, 0);
|
|
852
|
+
pb.main(function() {
|
|
853
|
+
this.$l.i = this.$builtins.globalInvocationId.x;
|
|
854
|
+
this.$if(pb.lessThan(this.i, pb.uint(this.count.x)), function() {
|
|
855
|
+
this.$l.d = sampleSceneSDF(this, this.points.at(this.i).xyz, levels);
|
|
856
|
+
pb.textureStore(this.result, pb.uvec2(this.i, 0), pb.vec4(this.d, 0, 0, 0));
|
|
857
|
+
});
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
export { GlobalSDF };
|
|
865
|
+
//# sourceMappingURL=global_sdf.js.map
|