@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,929 @@
|
|
|
1
|
+
import { Disposable, Vector3, Vector4, DRef, DEBUG_VIEWS, nextPowerOf2 } from '@zephyr3d/base';
|
|
2
|
+
import '../../values.js';
|
|
3
|
+
import { getDevice } from '../../app/api.js';
|
|
4
|
+
import '../../render/render_queue.js';
|
|
5
|
+
import { Primitive } from '../../render/primitive.js';
|
|
6
|
+
import '@zephyr3d/device';
|
|
7
|
+
import '../../material/shader/helper.js';
|
|
8
|
+
import '../../material/lambert.js';
|
|
9
|
+
import '../../material/blinn.js';
|
|
10
|
+
import '../../material/unlit.js';
|
|
11
|
+
import '../../material/meshmaterial.js';
|
|
12
|
+
import '../../material/particle.js';
|
|
13
|
+
import '../../material/subsurfaceprofile.js';
|
|
14
|
+
import '../../material/sssprofile.js';
|
|
15
|
+
import '../../material/sss.js';
|
|
16
|
+
import '../../material/eye.js';
|
|
17
|
+
import '../../material/hair.js';
|
|
18
|
+
import '../../material/hairstrand.js';
|
|
19
|
+
import '../../material/grassmaterial.js';
|
|
20
|
+
import '../../material/terrain-cm.js';
|
|
21
|
+
import '../../material/pbrmr.js';
|
|
22
|
+
import '../../material/pbrsg.js';
|
|
23
|
+
import '../../material/mtoon.js';
|
|
24
|
+
import '../../material/pbrblueprint.js';
|
|
25
|
+
import '../../material/sprite.js';
|
|
26
|
+
import '../../material/sprite_std.js';
|
|
27
|
+
import '../../utility/blueprint/material/texture.js';
|
|
28
|
+
import { fetchSampler } from '../../utility/misc.js';
|
|
29
|
+
import '../../render/rendergraph/forward_plus_builder.js';
|
|
30
|
+
import '../../render/lightpass.js';
|
|
31
|
+
import '../../render/sky.js';
|
|
32
|
+
import '../../render/clipmap.js';
|
|
33
|
+
import '../../render/envlight.js';
|
|
34
|
+
import '../../render/lod.js';
|
|
35
|
+
import '../../render/dualdepthpeeling_oit.js';
|
|
36
|
+
import '../../shaders/atmosphere.js';
|
|
37
|
+
import '../../render/water_interaction.js';
|
|
38
|
+
import '../../render/rendergraph/scene_render_context.js';
|
|
39
|
+
import '../../render/sdf/scene_sdf.js';
|
|
40
|
+
import '../../render/sdf/global_sdf.js';
|
|
41
|
+
import '../../render/gi/voxelize_pass.js';
|
|
42
|
+
import { WindField } from '../wind.js';
|
|
43
|
+
|
|
44
|
+
const WORKGROUP_SIZE = 8;
|
|
45
|
+
/**
|
|
46
|
+
* Largest placement window side, in cells. Bounds the per-frame work and the instance buffer
|
|
47
|
+
* when the draw distance is unlimited or the cells are tiny.
|
|
48
|
+
*/ const MAX_WINDOW_CELLS = 2048;
|
|
49
|
+
/**
|
|
50
|
+
* Width of the band, in density LOD hash units, over which a blade shrinks before the density
|
|
51
|
+
* LOD drops it. Hashes are scaled into [0, 1 - band) so that full density keeps every blade whole.
|
|
52
|
+
* @internal
|
|
53
|
+
*/ const GRASS_LOD_FADE_BAND = 0.05;
|
|
54
|
+
/** Fraction of the draw distance at which the density starts to fall */ const GRASS_LOD_START = 0.25;
|
|
55
|
+
/**
|
|
56
|
+
* Density LOD parameters shared by the grass material and the GPU placement: (distance at which
|
|
57
|
+
* the density starts to fall, fraction of blades kept at the draw distance). A fraction of 1
|
|
58
|
+
* disables it.
|
|
59
|
+
* @internal
|
|
60
|
+
*/ function grassDensityLod(drawDistance, farDensity) {
|
|
61
|
+
if (drawDistance <= 0 || farDensity >= 1) {
|
|
62
|
+
return [
|
|
63
|
+
0,
|
|
64
|
+
1
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
return [
|
|
68
|
+
drawDistance * GRASS_LOD_START,
|
|
69
|
+
Math.max(0.01, farDensity)
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
/** Vec4 per instance written by the placement pass for each layer kind */ const INSTANCE_VEC4 = {
|
|
73
|
+
card: 1,
|
|
74
|
+
blade: 4
|
|
75
|
+
};
|
|
76
|
+
/** Seed offset of the procedural blade hashes, clear of the card hashes of every layer */ const BLADE_SEED_BASE = 65536;
|
|
77
|
+
/**
|
|
78
|
+
* Fraction of the low detail distance over which the near blades make way for the far ones:
|
|
79
|
+
* three in four shrink away, the fourth widens and morphs into the low detail shape. The same
|
|
80
|
+
* last-quarter band as the draw distance fade.
|
|
81
|
+
* @internal
|
|
82
|
+
*/ const GRASS_BLADE_LOD_BAND = 0.25;
|
|
83
|
+
/** Stand-in for an unlimited distance in shader uniforms */ const NO_LIMIT = 1e30;
|
|
84
|
+
/**
|
|
85
|
+
* Distance at which procedural blades switch to the low detail level, or NO_LIMIT when they never
|
|
86
|
+
* do: zero disables the low detail level, and so does a distance past the draw distance.
|
|
87
|
+
* @internal
|
|
88
|
+
*/ function grassBladeLodDistance(lodDistance, drawDistance) {
|
|
89
|
+
return lodDistance > 0 && (drawDistance <= 0 || lodDistance < drawDistance) ? lodDistance : NO_LIMIT;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Number of culling planes of the placement pass: the six of a view frustum, or up to the twelve
|
|
93
|
+
* of a shadow caster culling volume (see GrassRenderer). Unused ones are planes everything is
|
|
94
|
+
* inside of.
|
|
95
|
+
* @internal
|
|
96
|
+
*/ const GRASS_CULL_PLANES = 12;
|
|
97
|
+
/** Samples along the line of sight of the terrain occlusion test, one per workgroup thread */ const OCCLUSION_SAMPLES = WORKGROUP_SIZE * WORKGROUP_SIZE;
|
|
98
|
+
/**
|
|
99
|
+
* Terrain occlusion culling mode of the GPU grass placement
|
|
100
|
+
* @internal
|
|
101
|
+
*/ const GrassOcclusionMode = {
|
|
102
|
+
Off: 0,
|
|
103
|
+
Cull: 1,
|
|
104
|
+
/** Keep occluded blades but flag them, see ClipmapGrassMaterial.occlusionDebug */ Debug: 2
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* Grass blade placement hash, shared by the CPU and GPU paths.
|
|
108
|
+
*
|
|
109
|
+
* Returns a value in [0, 1) with 24 bits of precision: that is all a float32 can hold, so
|
|
110
|
+
* keeping the CPU side to the same bits makes both paths place exactly the same blades.
|
|
111
|
+
*
|
|
112
|
+
* @internal
|
|
113
|
+
*/ function grassHash(x, z, seed) {
|
|
114
|
+
let h = Math.imul(x, 0x27d4eb2d) ^ Math.imul(z, 0x165667b1) ^ Math.imul(seed, 0x9e3779b9) | 0;
|
|
115
|
+
h = Math.imul(h ^ h >>> 15, 0x85ebca6b);
|
|
116
|
+
h = Math.imul(h ^ h >>> 13, 0xc2b2ae35);
|
|
117
|
+
h ^= h >>> 16;
|
|
118
|
+
return (h >>> 8) / 16777216;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* GPU placement and culling of the blades of one grass layer (WebGPU with indirect draw only).
|
|
122
|
+
*
|
|
123
|
+
* Blade placement is procedural - a hash per placement cell and a bilinear density lookup - so
|
|
124
|
+
* instead of keeping instances for the whole terrain, a compute pass evaluates the cells around
|
|
125
|
+
* the camera every frame, keeps the blades inside the frustum and the draw distance, and appends
|
|
126
|
+
* them to an instance buffer whose count lands in an indirect draw argument buffer.
|
|
127
|
+
*
|
|
128
|
+
* The output uses the instance format of the CPU path (u, v, facing angle, density LOD hash), so the grass material
|
|
129
|
+
* is shared. Each dispatched thread emits at most one blade and the instance buffer holds one
|
|
130
|
+
* per thread, so the output can never overflow.
|
|
131
|
+
*
|
|
132
|
+
* Procedural blades have two detail levels, after "Procedural Grass in Ghost of Tsushima"
|
|
133
|
+
* (Wohllaib, GDC 2021): low detail blades are spaced twice as far apart, and the high detail
|
|
134
|
+
* blades thin out by three quarters before the switch so the two meet seamlessly. Here the
|
|
135
|
+
* dispatch has two layers: z = 0 places a blade per cell around the camera, up to the low detail
|
|
136
|
+
* distance, into the high detail list; z = 1 places one blade per 2x2 block of cells beyond it
|
|
137
|
+
* into the low detail list. The blade kept for a block is one of its four cells picked by a
|
|
138
|
+
* hash, placed exactly as the near layer places it, so a blade moving from one list to the other
|
|
139
|
+
* stays where it is. Both lists share the instance buffer, the low one from `lowListBase` on,
|
|
140
|
+
* and each has its own indirect draw arguments.
|
|
141
|
+
*
|
|
142
|
+
* Placements go to numbered targets, each with its own lists: target 0 holds the blades seen
|
|
143
|
+
* from the camera, the others the blades casting shadows into a shadow map. A shadow target is
|
|
144
|
+
* placed around the same camera, so it picks the same blades at the same detail levels, but
|
|
145
|
+
* culls them by the volume the shadow casters can be in instead of the view frustum.
|
|
146
|
+
*
|
|
147
|
+
* @internal
|
|
148
|
+
*/ class GrassGpuPlacement extends Disposable {
|
|
149
|
+
static _programs = {};
|
|
150
|
+
static _cameraPos = new Vector3();
|
|
151
|
+
static _planes = new Float32Array(GRASS_CULL_PLANES * 4);
|
|
152
|
+
_densityTexture;
|
|
153
|
+
_kind;
|
|
154
|
+
_targets;
|
|
155
|
+
_baseVertexBuffer;
|
|
156
|
+
_shape0;
|
|
157
|
+
_shape1;
|
|
158
|
+
_clump0;
|
|
159
|
+
_clump1;
|
|
160
|
+
static _calmWind = new Float32Array(32);
|
|
161
|
+
_indexBuffer;
|
|
162
|
+
_args;
|
|
163
|
+
/** Index count of each detail level; they follow each other in the index buffer */ _lodIndexCounts;
|
|
164
|
+
_window;
|
|
165
|
+
/** Far window, in 2x2 blocks of cells (procedural blades only) */ _window2;
|
|
166
|
+
/** (low detail distance, first instance of the low detail list, shadow fade start, 1 / fade length) */ _lod;
|
|
167
|
+
/** Camera forward axis and offset, see generate() */ _viewDepth;
|
|
168
|
+
static _forward = new Vector3();
|
|
169
|
+
_cells;
|
|
170
|
+
_params;
|
|
171
|
+
_posScale;
|
|
172
|
+
_camera;
|
|
173
|
+
_occlusion;
|
|
174
|
+
_heightInfo;
|
|
175
|
+
_densityLod;
|
|
176
|
+
/**
|
|
177
|
+
* Whether the current device can run the GPU path
|
|
178
|
+
*/ static isSupported() {
|
|
179
|
+
const device = getDevice();
|
|
180
|
+
return device.type === 'webgpu' && device.getDeviceCaps().miscCaps.supportDrawIndirect;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* @param kind - Kind of blades placed
|
|
184
|
+
* @param baseVertexBuffer - Vertex buffer of the drawn blade. Procedural blades read nothing
|
|
185
|
+
* from it; a draw still needs a vertex layout.
|
|
186
|
+
* @param indexBuffer - Index buffer of the drawn blade
|
|
187
|
+
* @param lodIndexCounts - Index count of each detail level, which follow each other in the index
|
|
188
|
+
* buffer. Procedural blades have two; defaults to the whole buffer as one level.
|
|
189
|
+
*/ constructor(kind, baseVertexBuffer, indexBuffer, lodIndexCounts){
|
|
190
|
+
super();
|
|
191
|
+
this._kind = kind;
|
|
192
|
+
this._lodIndexCounts = lodIndexCounts ?? [
|
|
193
|
+
indexBuffer.length
|
|
194
|
+
];
|
|
195
|
+
this._shape0 = new Vector4();
|
|
196
|
+
this._shape1 = new Vector4();
|
|
197
|
+
this._clump0 = new Vector4();
|
|
198
|
+
this._clump1 = new Vector4();
|
|
199
|
+
this._densityTexture = new DRef();
|
|
200
|
+
this._targets = [];
|
|
201
|
+
this._baseVertexBuffer = new DRef(baseVertexBuffer);
|
|
202
|
+
this._indexBuffer = new DRef(indexBuffer);
|
|
203
|
+
// Five draw arguments per detail level, then with debug views the same for the wireframe edges
|
|
204
|
+
this._args = new Uint32Array(this._lodIndexCounts.length * 5 * (DEBUG_VIEWS ? 2 : 1));
|
|
205
|
+
this._window = new Vector4();
|
|
206
|
+
this._window2 = new Vector4();
|
|
207
|
+
this._lod = new Vector4();
|
|
208
|
+
this._viewDepth = new Vector4();
|
|
209
|
+
this._cells = new Vector4();
|
|
210
|
+
this._params = new Vector4();
|
|
211
|
+
this._posScale = new Vector4();
|
|
212
|
+
this._camera = new Vector4();
|
|
213
|
+
this._occlusion = new Vector4();
|
|
214
|
+
this._heightInfo = new Vector4();
|
|
215
|
+
this._densityLod = new Vector4();
|
|
216
|
+
}
|
|
217
|
+
setBaseVertexBuffer(baseVertexBuffer) {
|
|
218
|
+
if (baseVertexBuffer !== this._baseVertexBuffer.get()) {
|
|
219
|
+
this._baseVertexBuffer.set(baseVertexBuffer);
|
|
220
|
+
for (const target of this._targets){
|
|
221
|
+
target.primitive.dispose();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Uploads the whole density map, recreating the texture when its size changed
|
|
227
|
+
* @param width - Density map width in texels
|
|
228
|
+
* @param height - Density map height in texels
|
|
229
|
+
* @param data - Density data, one byte per texel
|
|
230
|
+
*/ setDensity(width, height, data) {
|
|
231
|
+
let tex = this._densityTexture.get();
|
|
232
|
+
if (!tex || tex.width !== width || tex.height !== height) {
|
|
233
|
+
tex = getDevice().createTexture2D('r8unorm', width, height, {
|
|
234
|
+
mipmapping: false
|
|
235
|
+
});
|
|
236
|
+
tex.name = 'GrassDensity';
|
|
237
|
+
this._densityTexture.set(tex);
|
|
238
|
+
for (const target of this._targets){
|
|
239
|
+
target.bindGroup.dispose();
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
// Texture uploads take views of plain array buffers only
|
|
243
|
+
const bytes = data.buffer instanceof ArrayBuffer ? data : new Uint8Array(data);
|
|
244
|
+
tex.update(bytes, 0, 0, width, height);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Uploads a rectangle of the density map
|
|
248
|
+
* @param data - The whole density map, one byte per texel
|
|
249
|
+
* @param width - Density map width in texels
|
|
250
|
+
* @param x0 - Minimum x texel (inclusive)
|
|
251
|
+
* @param z0 - Minimum z texel (inclusive)
|
|
252
|
+
* @param x1 - Maximum x texel (exclusive)
|
|
253
|
+
* @param z1 - Maximum z texel (exclusive)
|
|
254
|
+
*/ updateDensityRegion(data, width, x0, z0, x1, z1) {
|
|
255
|
+
const tex = this._densityTexture.get();
|
|
256
|
+
if (!tex || x1 <= x0 || z1 <= z0) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
const w = x1 - x0;
|
|
260
|
+
const h = z1 - z0;
|
|
261
|
+
const rect = new Uint8Array(w * h);
|
|
262
|
+
for(let z = 0; z < h; z++){
|
|
263
|
+
rect.set(data.subarray((z0 + z) * width + x0, (z0 + z) * width + x1), z * w);
|
|
264
|
+
}
|
|
265
|
+
tex.update(rect, x0, z0, w, h);
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Places and culls the blades seen from a camera. Runs a compute pass, so it must be called
|
|
269
|
+
* outside of render passes, before the draws that consume the result.
|
|
270
|
+
* @param target - Target the blades are placed into, see draw()
|
|
271
|
+
* @param cullPlanes - GRASS_CULL_PLANES planes (a, b, c, d) the blades are culled by instead of
|
|
272
|
+
* the camera's frustum, whose normals point inward
|
|
273
|
+
* @param shadowFade - Procedural blades only: (start, end) view depth over which the blades thin
|
|
274
|
+
* out to none, each leaving at its own depth by a hash. Lets the blades casting shadows fade
|
|
275
|
+
* out before the last cascade they are drawn into instead of stopping at its far split.
|
|
276
|
+
*/ generate(camera, terrain, seed, cellsPerTexel, drawDistance, bladeWidth, bladeHeight, occlusionMode, farDensity, bladeShape, windParams, target = 0, cullPlanes, shadowFade) {
|
|
277
|
+
const placementTarget = this.getTarget(target);
|
|
278
|
+
const density = this._densityTexture.get();
|
|
279
|
+
const heightMap = terrain.heightMap;
|
|
280
|
+
// Draw arguments of each detail level, whose indices follow each other: (index count,
|
|
281
|
+
// instance count, first index, base vertex, first instance)
|
|
282
|
+
const levels = this._lodIndexCounts.length;
|
|
283
|
+
let firstIndex = 0;
|
|
284
|
+
for(let i = 0; i < levels; i++){
|
|
285
|
+
this._args.set([
|
|
286
|
+
this._lodIndexCounts[i],
|
|
287
|
+
0,
|
|
288
|
+
firstIndex,
|
|
289
|
+
0,
|
|
290
|
+
0
|
|
291
|
+
], i * 5);
|
|
292
|
+
if (DEBUG_VIEWS) {
|
|
293
|
+
// Edge index buffers hold 6 indices per triangle, twice the triangle indices
|
|
294
|
+
this._args.set([
|
|
295
|
+
this._lodIndexCounts[i] * 2,
|
|
296
|
+
0,
|
|
297
|
+
firstIndex * 2,
|
|
298
|
+
0,
|
|
299
|
+
0
|
|
300
|
+
], (levels + i) * 5);
|
|
301
|
+
}
|
|
302
|
+
firstIndex += this._lodIndexCounts[i];
|
|
303
|
+
}
|
|
304
|
+
placementTarget.argsBuffer.get().bufferSubData(0, this._args.subarray(0, levels * 5));
|
|
305
|
+
placementTarget.edgeArgsBuffer.get()?.bufferSubData(0, this._args.subarray(levels * 5));
|
|
306
|
+
if (!density || !heightMap) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const blade = this._kind === 'blade' && !!bladeShape;
|
|
310
|
+
const region = terrain.worldRegion;
|
|
311
|
+
const cellsW = density.width * cellsPerTexel;
|
|
312
|
+
const cellsH = density.height * cellsPerTexel;
|
|
313
|
+
const cellSizeX = (region.z - region.x) / cellsW;
|
|
314
|
+
const cellSizeZ = (region.w - region.y) / cellsH;
|
|
315
|
+
const cameraPos = camera.getWorldPosition(GrassGpuPlacement._cameraPos);
|
|
316
|
+
const centerX = (cameraPos.x - region.x) / cellSizeX;
|
|
317
|
+
const centerZ = (cameraPos.z - region.y) / cellSizeZ;
|
|
318
|
+
const drawRadius = drawDistance > 0 ? drawDistance : Infinity;
|
|
319
|
+
// Near window: cells within the draw distance, or within the low detail distance of blades
|
|
320
|
+
// that have one; far window: 2x2 blocks of cells from there on to the draw distance
|
|
321
|
+
const lodDistance = blade ? grassBladeLodDistance(bladeShape[15], drawDistance) : NO_LIMIT;
|
|
322
|
+
const nearRadius = Math.min(drawRadius, lodDistance);
|
|
323
|
+
const near = GrassGpuPlacement.cellWindow(centerX, centerZ, nearRadius / cellSizeX, nearRadius / cellSizeZ, cellsW, cellsH, 1);
|
|
324
|
+
const far = lodDistance < NO_LIMIT && lodDistance < drawRadius ? GrassGpuPlacement.cellWindow(centerX, centerZ, drawRadius / cellSizeX, drawRadius / cellSizeZ, cellsW, cellsH, 2) : [
|
|
325
|
+
0,
|
|
326
|
+
0,
|
|
327
|
+
0,
|
|
328
|
+
0
|
|
329
|
+
];
|
|
330
|
+
const nearCount = near[2] * near[3];
|
|
331
|
+
const farCount = far[2] * far[3];
|
|
332
|
+
if (nearCount <= 0 && farCount <= 0) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
// Each list holds at most one blade per thread of its layer
|
|
336
|
+
this.ensureCapacity(placementTarget, Math.max(nearCount, farCount));
|
|
337
|
+
const bindGroup = this.getBindGroup(placementTarget);
|
|
338
|
+
this._window.setXYZW(near[0], near[1], near[2], near[3]);
|
|
339
|
+
this._window2.setXYZW(far[0], far[1], far[2], far[3]);
|
|
340
|
+
// (fade start, 1 / fade length); a zero length leaves every blade in
|
|
341
|
+
const fadeLength = shadowFade ? shadowFade[1] - shadowFade[0] : 0;
|
|
342
|
+
this._lod.setXYZW(lodDistance, placementTarget.capacity, shadowFade ? shadowFade[0] : 0, fadeLength > 0 ? 1 / fadeLength : 0);
|
|
343
|
+
// View depth of a point: dot(xyz, p) + w, along the camera's forward axis
|
|
344
|
+
const m = camera.worldMatrix;
|
|
345
|
+
const forward = GrassGpuPlacement._forward.setXYZ(-m.m02, -m.m12, -m.m22).inplaceNormalize();
|
|
346
|
+
this._viewDepth.setXYZW(forward.x, forward.y, forward.z, -Vector3.dot(forward, cameraPos));
|
|
347
|
+
this._cells.setXYZW(cellsW, cellsH, density.width, density.height);
|
|
348
|
+
// Bounding sphere of a blade, centred halfway up; the density LOD widens distant blades
|
|
349
|
+
const lod = grassDensityLod(drawDistance, farDensity);
|
|
350
|
+
this._densityLod.setXYZW(lod[0], lod[1], 0, 0);
|
|
351
|
+
if (blade) {
|
|
352
|
+
// A blade reaches at most its tallest height from its base in any direction, plus half its
|
|
353
|
+
// width, which grows fourfold toward the low detail level
|
|
354
|
+
const maxHeight = bladeShape[0] * (1 + Math.abs(bladeShape[1])) * (1 + Math.abs(bladeShape[9])) * (1 + bladeShape[14]);
|
|
355
|
+
const maxWidth = bladeShape[2] * (1 + Math.abs(bladeShape[3])) * 4;
|
|
356
|
+
this._shape0.setXYZW(bladeShape[0], bladeShape[1], bladeShape[2], bladeShape[3]);
|
|
357
|
+
this._shape1.setXYZW(bladeShape[4], bladeShape[5], bladeShape[6], bladeShape[7]);
|
|
358
|
+
this._clump0.setXYZW(1 / Math.max(1e-3, bladeShape[8]), bladeShape[9], bladeShape[10], bladeShape[11]);
|
|
359
|
+
this._clump1.setXYZW(bladeShape[12], bladeShape[13], 0, 0);
|
|
360
|
+
this._params.setXYZW(seed * 4, drawDistance, maxHeight + maxWidth * 0.5, 0);
|
|
361
|
+
bladeHeight = maxHeight;
|
|
362
|
+
} else {
|
|
363
|
+
const maxWidth = bladeWidth / lod[1];
|
|
364
|
+
// The wind turns a card about its base, which takes its top up to 1.12 times its height
|
|
365
|
+
// from the centre of the card
|
|
366
|
+
const radius = windParams && windParams[2] > 0 ? maxWidth * 0.5 + bladeHeight * 1.12 : Math.sqrt(maxWidth * maxWidth * 0.25 + bladeHeight * bladeHeight * 0.25);
|
|
367
|
+
this._params.setXYZW(seed * 4, drawDistance, radius, bladeHeight * 0.5);
|
|
368
|
+
}
|
|
369
|
+
this._posScale.setXYZW(terrain.scale.x, terrain.scale.y, terrain.scale.z, terrain.worldMatrix.m13);
|
|
370
|
+
this._camera.setXYZW(cameraPos.x, cameraPos.y, cameraPos.z, 0);
|
|
371
|
+
// Terrain occlusion needs the height pyramid, and heights that grow upwards
|
|
372
|
+
const pyramid = terrain.heightPyramid;
|
|
373
|
+
const occlusion = pyramid && terrain.scale.y > 0 ? occlusionMode : GrassOcclusionMode.Off;
|
|
374
|
+
const bounds = terrain.getWorldBoundingVolume()?.toAABB();
|
|
375
|
+
// Absorbs half float and interpolation error in the heights
|
|
376
|
+
const epsilon = bounds ? (bounds.maxPoint.y - bounds.minPoint.y) * 1e-3 + 1e-4 : 1e-3;
|
|
377
|
+
this._occlusion.setXYZW(occlusion, terrain.clipmapTileResolution, epsilon, bladeHeight);
|
|
378
|
+
this._heightInfo.setXYZW(heightMap.width, heightMap.height, pyramid?.mipLevelCount ?? 1, Math.max((region.z - region.x) / heightMap.width, (region.w - region.y) / heightMap.height));
|
|
379
|
+
// Planes masked out by the camera become ones everything is inside of
|
|
380
|
+
let planes = GrassGpuPlacement._planes;
|
|
381
|
+
if (cullPlanes) {
|
|
382
|
+
planes = cullPlanes;
|
|
383
|
+
} else {
|
|
384
|
+
const frustumPlanes = camera.frustum.planes;
|
|
385
|
+
const mask = camera.clipMask || 0x3f;
|
|
386
|
+
for(let i = 0; i < GRASS_CULL_PLANES; i++){
|
|
387
|
+
const p = frustumPlanes[i];
|
|
388
|
+
const enabled = i < 6 && !!(mask & 1 << i);
|
|
389
|
+
planes[i * 4 + 0] = enabled ? p.a : 0;
|
|
390
|
+
planes[i * 4 + 1] = enabled ? p.b : 0;
|
|
391
|
+
planes[i * 4 + 2] = enabled ? p.c : 0;
|
|
392
|
+
planes[i * 4 + 3] = enabled ? p.d : 1e30;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
bindGroup.setValue('window', this._window);
|
|
396
|
+
bindGroup.setValue('cells', this._cells);
|
|
397
|
+
bindGroup.setValue('params', this._params);
|
|
398
|
+
bindGroup.setValue('region', region);
|
|
399
|
+
bindGroup.setValue('posScale', this._posScale);
|
|
400
|
+
bindGroup.setValue('cameraPos', this._camera);
|
|
401
|
+
bindGroup.setValue('planes', planes);
|
|
402
|
+
bindGroup.setValue('occlusion', this._occlusion);
|
|
403
|
+
bindGroup.setValue('densityLod', this._densityLod);
|
|
404
|
+
bindGroup.setValue('heightInfo', this._heightInfo);
|
|
405
|
+
if (this._kind === 'blade') {
|
|
406
|
+
bindGroup.setValue('window2', this._window2);
|
|
407
|
+
bindGroup.setValue('lod', this._lod);
|
|
408
|
+
bindGroup.setValue('viewDepth', this._viewDepth);
|
|
409
|
+
bindGroup.setValue('shape0', this._shape0);
|
|
410
|
+
bindGroup.setValue('shape1', this._shape1);
|
|
411
|
+
bindGroup.setValue('clump0', this._clump0);
|
|
412
|
+
bindGroup.setValue('clump1', this._clump1);
|
|
413
|
+
bindGroup.setValue('wind', windParams ?? GrassGpuPlacement._calmWind);
|
|
414
|
+
}
|
|
415
|
+
// Any texture of the right sample type does when occlusion is off. Declared unfilterable
|
|
416
|
+
// (read with textureLoad only), so the sampler bound with it must be a non-filtering one
|
|
417
|
+
// whatever the texture's default is: rg32f counts as filterable where the device has
|
|
418
|
+
// float32-filterable.
|
|
419
|
+
bindGroup.setTexture('heightPyramid', pyramid ?? GrassGpuPlacement.getDummyPyramid(), fetchSampler('clamp_nearest_nomip'));
|
|
420
|
+
bindGroup.setTexture('density', density);
|
|
421
|
+
bindGroup.setTexture('heightMap', heightMap, fetchSampler('clamp_linear_nomip'));
|
|
422
|
+
const device = getDevice();
|
|
423
|
+
device.setProgram(GrassGpuPlacement.getProgram(this._kind));
|
|
424
|
+
device.setBindGroup(0, bindGroup);
|
|
425
|
+
// The far layer, when there is one, runs as z = 1 of the same dispatch
|
|
426
|
+
device.compute(Math.ceil(Math.max(near[2], far[2]) / WORKGROUP_SIZE), Math.ceil(Math.max(near[3], far[3]) / WORKGROUP_SIZE), farCount > 0 ? 2 : 1);
|
|
427
|
+
// The instance counts placement wrote, for drawing the edges in a wireframe view
|
|
428
|
+
const edgeArgs = placementTarget.edgeArgsBuffer.get();
|
|
429
|
+
if (edgeArgs) {
|
|
430
|
+
for(let i = 0; i < this._lodIndexCounts.length; i++){
|
|
431
|
+
device.copyBuffer(placementTarget.argsBuffer.get(), edgeArgs, (i * 5 + 1) * 4, (i * 5 + 1) * 4, 4);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* Window of placement units within a radius of a point, clipped to the grid and to
|
|
437
|
+
* MAX_WINDOW_CELLS units a side: (first x, first z, units wide, units high)
|
|
438
|
+
* @param centerX - Point x, in cells
|
|
439
|
+
* @param centerZ - Point z, in cells
|
|
440
|
+
* @param radiusX - Radius along x, in cells
|
|
441
|
+
* @param radiusZ - Radius along z, in cells
|
|
442
|
+
* @param cellsW - Grid width, in cells
|
|
443
|
+
* @param cellsH - Grid height, in cells
|
|
444
|
+
* @param unit - Placement unit side, in cells
|
|
445
|
+
*/ static cellWindow(centerX, centerZ, radiusX, radiusZ, cellsW, cellsH, unit) {
|
|
446
|
+
const halfMax = MAX_WINDOW_CELLS / 2;
|
|
447
|
+
const cx = centerX / unit;
|
|
448
|
+
const cz = centerZ / unit;
|
|
449
|
+
const rx = Math.min(radiusX / unit, halfMax);
|
|
450
|
+
const rz = Math.min(radiusZ / unit, halfMax);
|
|
451
|
+
const x0 = Math.max(0, Math.floor(cx - rx));
|
|
452
|
+
const x1 = Math.min(Math.ceil(cellsW / unit), Math.ceil(cx + rx));
|
|
453
|
+
const z0 = Math.max(0, Math.floor(cz - rz));
|
|
454
|
+
const z1 = Math.min(Math.ceil(cellsH / unit), Math.ceil(cz + rz));
|
|
455
|
+
return [
|
|
456
|
+
x0,
|
|
457
|
+
z0,
|
|
458
|
+
Math.max(0, x1 - x0),
|
|
459
|
+
Math.max(0, z1 - z0)
|
|
460
|
+
];
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Instance buffer written by the last generate() call into a target
|
|
464
|
+
* @param target - The target
|
|
465
|
+
*/ getInstanceBuffer(target = 0) {
|
|
466
|
+
return this._targets[target]?.instanceBuffer.get() ?? null;
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* First instance of the low detail list in the instance buffer of a target (procedural blades
|
|
470
|
+
* only); the high detail list starts at 0
|
|
471
|
+
* @param target - The target
|
|
472
|
+
*/ getLowListBase(target = 0) {
|
|
473
|
+
return this._targets[target]?.capacity ?? 0;
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Draws the blades of the last generate() call into a target; the material must be bound
|
|
477
|
+
* @param target - The target
|
|
478
|
+
*/ draw(target = 0) {
|
|
479
|
+
const placementTarget = this._targets[target];
|
|
480
|
+
if (!placementTarget) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
let primitive = placementTarget.primitive.get();
|
|
484
|
+
if (!primitive) {
|
|
485
|
+
const instanceBuffer = placementTarget.instanceBuffer.get();
|
|
486
|
+
if (!instanceBuffer) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
primitive = new Primitive();
|
|
490
|
+
primitive.setVertexBuffer(this._baseVertexBuffer.get());
|
|
491
|
+
if (this._kind === 'card') {
|
|
492
|
+
primitive.setVertexBuffer(instanceBuffer, 'instance');
|
|
493
|
+
}
|
|
494
|
+
primitive.setIndexBuffer(this._indexBuffer.get());
|
|
495
|
+
primitive.primitiveType = 'triangle-list';
|
|
496
|
+
primitive.indexStart = 0;
|
|
497
|
+
primitive.indexCount = this._indexBuffer.get().length;
|
|
498
|
+
placementTarget.primitive.set(primitive);
|
|
499
|
+
}
|
|
500
|
+
// One draw per detail level; the arguments pick its range of the index buffer
|
|
501
|
+
const edgeArgs = placementTarget.edgeArgsBuffer.get();
|
|
502
|
+
for(let i = 0; i < this._lodIndexCounts.length; i++){
|
|
503
|
+
primitive.drawIndirect(placementTarget.argsBuffer.get(), i * 20, edgeArgs ? i * 20 : -1, edgeArgs ?? undefined);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Releases the targets from a number on, which are no longer placed into
|
|
508
|
+
* @param count - Number of targets kept
|
|
509
|
+
*/ trimTargets(count) {
|
|
510
|
+
while(this._targets.length > Math.max(1, count)){
|
|
511
|
+
GrassGpuPlacement.disposeTarget(this._targets.pop());
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
getTarget(index) {
|
|
515
|
+
while(this._targets.length <= index){
|
|
516
|
+
this._targets.push({
|
|
517
|
+
instanceBuffer: new DRef(),
|
|
518
|
+
argsBuffer: new DRef(// Dynamic: the arguments are reset every frame, and a dynamic buffer reuses its upload
|
|
519
|
+
// staging buffers instead of creating and destroying one per upload
|
|
520
|
+
getDevice().createBuffer(this._lodIndexCounts.length * 20, {
|
|
521
|
+
usage: 'indirect',
|
|
522
|
+
storage: true,
|
|
523
|
+
dynamic: true
|
|
524
|
+
})),
|
|
525
|
+
edgeArgsBuffer: new DRef(DEBUG_VIEWS ? getDevice().createBuffer(this._lodIndexCounts.length * 20, {
|
|
526
|
+
usage: 'indirect',
|
|
527
|
+
dynamic: true
|
|
528
|
+
}) : null),
|
|
529
|
+
bindGroup: new DRef(),
|
|
530
|
+
primitive: new DRef(),
|
|
531
|
+
capacity: 0
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
return this._targets[index];
|
|
535
|
+
}
|
|
536
|
+
static disposeTarget(target) {
|
|
537
|
+
target.instanceBuffer.dispose();
|
|
538
|
+
target.argsBuffer.dispose();
|
|
539
|
+
target.edgeArgsBuffer.dispose();
|
|
540
|
+
target.bindGroup.dispose();
|
|
541
|
+
target.primitive.dispose();
|
|
542
|
+
}
|
|
543
|
+
ensureCapacity(target, numInstances) {
|
|
544
|
+
if (numInstances > target.capacity) {
|
|
545
|
+
target.capacity = nextPowerOf2(numInstances);
|
|
546
|
+
const device = getDevice();
|
|
547
|
+
const buffer = this._kind === 'card' ? device.createVertexBuffer('tex1_f32x4', new Float32Array(target.capacity * 4), {
|
|
548
|
+
storage: true
|
|
549
|
+
}) : device.createBuffer(target.capacity * 2 * INSTANCE_VEC4.blade * 16, {
|
|
550
|
+
usage: 'uniform',
|
|
551
|
+
storage: true,
|
|
552
|
+
dynamic: false,
|
|
553
|
+
managed: false
|
|
554
|
+
});
|
|
555
|
+
target.instanceBuffer.set(buffer);
|
|
556
|
+
target.primitive.dispose();
|
|
557
|
+
target.bindGroup.dispose();
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
getBindGroup(target) {
|
|
561
|
+
let bindGroup = target.bindGroup.get();
|
|
562
|
+
if (!bindGroup) {
|
|
563
|
+
bindGroup = getDevice().createBindGroup(GrassGpuPlacement.getProgram(this._kind).bindGroupLayouts[0]);
|
|
564
|
+
bindGroup.setBuffer('instances', target.instanceBuffer.get());
|
|
565
|
+
bindGroup.setBuffer('args', target.argsBuffer.get());
|
|
566
|
+
target.bindGroup.set(bindGroup);
|
|
567
|
+
}
|
|
568
|
+
return bindGroup;
|
|
569
|
+
}
|
|
570
|
+
static _dummyPyramid = null;
|
|
571
|
+
static getDummyPyramid() {
|
|
572
|
+
if (!this._dummyPyramid) {
|
|
573
|
+
this._dummyPyramid = getDevice().createTexture2D('rg32f', 1, 1, {
|
|
574
|
+
mipmapping: false
|
|
575
|
+
});
|
|
576
|
+
this._dummyPyramid.update(new Float32Array(2), 0, 0, 1, 1);
|
|
577
|
+
}
|
|
578
|
+
return this._dummyPyramid;
|
|
579
|
+
}
|
|
580
|
+
static getProgram(kind) {
|
|
581
|
+
let program = this._programs[kind];
|
|
582
|
+
if (!program) {
|
|
583
|
+
const blade = kind === 'blade';
|
|
584
|
+
program = getDevice().buildComputeProgram({
|
|
585
|
+
label: blade ? 'GrassBladePlacement' : 'GrassPlacement',
|
|
586
|
+
workgroupSize: [
|
|
587
|
+
WORKGROUP_SIZE,
|
|
588
|
+
WORKGROUP_SIZE,
|
|
589
|
+
1
|
|
590
|
+
],
|
|
591
|
+
compute (pb) {
|
|
592
|
+
// (first cell x, first cell z, cells wide, cells high)
|
|
593
|
+
this.window = pb.vec4().uniform(0);
|
|
594
|
+
// (cells x, cells z, density width, density height)
|
|
595
|
+
this.cells = pb.vec4().uniform(0);
|
|
596
|
+
// (seed, draw distance, blade bounding radius, blade centre height)
|
|
597
|
+
this.params = pb.vec4().uniform(0);
|
|
598
|
+
this.region = pb.vec4().uniform(0);
|
|
599
|
+
// (terrain scale xyz, terrain origin y)
|
|
600
|
+
this.posScale = pb.vec4().uniform(0);
|
|
601
|
+
this.cameraPos = pb.vec4().uniform(0);
|
|
602
|
+
this.planes = pb.vec4[GRASS_CULL_PLANES]().uniform(0);
|
|
603
|
+
// (mode, clipmap tile resolution, height epsilon, blade height)
|
|
604
|
+
this.occlusion = pb.vec4().uniform(0);
|
|
605
|
+
// (density falloff start distance, density at the draw distance, unused, unused)
|
|
606
|
+
this.densityLod = pb.vec4().uniform(0);
|
|
607
|
+
// (height map width, height map height, pyramid mip count, grid cell size)
|
|
608
|
+
this.heightInfo = pb.vec4().uniform(0);
|
|
609
|
+
if (blade) {
|
|
610
|
+
// Far window: (first block x, first block z, blocks wide, blocks high), 2x2 cells a block
|
|
611
|
+
this.window2 = pb.vec4().uniform(0);
|
|
612
|
+
// (low detail distance, first instance of the low detail list, shadow fade start depth,
|
|
613
|
+
// 1 / shadow fade length or 0 for no fade)
|
|
614
|
+
this.lod = pb.vec4().uniform(0);
|
|
615
|
+
// (camera forward, -dot(forward, camera position)): view depth of a point
|
|
616
|
+
this.viewDepth = pb.vec4().uniform(0);
|
|
617
|
+
// See GrassBladeShape
|
|
618
|
+
this.shape0 = pb.vec4().uniform(0);
|
|
619
|
+
this.shape1 = pb.vec4().uniform(0);
|
|
620
|
+
// (1 / clump size, height variation, pull to the clump point, same direction)
|
|
621
|
+
this.clump0 = pb.vec4().uniform(0);
|
|
622
|
+
// (face away from the clump point, wind facing, unused, unused)
|
|
623
|
+
this.clump1 = pb.vec4().uniform(0);
|
|
624
|
+
// Scene wind, current frame then previous frame, see WindField.shaderParams
|
|
625
|
+
this.wind = pb.vec4[8]().uniform(0);
|
|
626
|
+
}
|
|
627
|
+
this.heightPyramid = pb.tex2D().sampleType('unfilterable-float').uniform(0);
|
|
628
|
+
this.occludedFlag = pb.atomic_uint().workgroup();
|
|
629
|
+
this.density = pb.tex2D().uniform(0);
|
|
630
|
+
this.heightMap = pb.tex2D().uniform(0);
|
|
631
|
+
this.instances = pb.vec4[0]().storageBuffer(0);
|
|
632
|
+
this.args = pb.atomic_uint[0]().storageBuffer(0);
|
|
633
|
+
// See grassHash(); unsigned integer arithmetic wraps like Math.imul
|
|
634
|
+
pb.func('grassHash', [
|
|
635
|
+
pb.uint('x'),
|
|
636
|
+
pb.uint('z'),
|
|
637
|
+
pb.uint('s')
|
|
638
|
+
], function() {
|
|
639
|
+
this.$l.h = pb.compXor(pb.compXor(pb.mul(this.x, pb.uint(0x27d4eb2d)), pb.mul(this.z, pb.uint(0x165667b1))), pb.mul(this.s, pb.uint(0x9e3779b9)));
|
|
640
|
+
this.h = pb.mul(pb.compXor(this.h, pb.sar(this.h, 15)), pb.uint(0x85ebca6b));
|
|
641
|
+
this.h = pb.mul(pb.compXor(this.h, pb.sar(this.h, 13)), pb.uint(0xc2b2ae35));
|
|
642
|
+
this.h = pb.compXor(this.h, pb.sar(this.h, 16));
|
|
643
|
+
this.$return(pb.mul(pb.float(pb.sar(this.h, 8)), 1 / 16777216));
|
|
644
|
+
});
|
|
645
|
+
// Bilinear density, the same filtering as GrassLayer.sampleDensity()
|
|
646
|
+
pb.func('sampleDensity', [
|
|
647
|
+
pb.vec2('uv')
|
|
648
|
+
], function() {
|
|
649
|
+
this.$l.size = this.cells.zw;
|
|
650
|
+
this.$l.p = pb.sub(pb.mul(this.uv, this.size), pb.vec2(0.5));
|
|
651
|
+
this.$l.i = pb.floor(this.p);
|
|
652
|
+
this.$l.f = pb.sub(this.p, this.i);
|
|
653
|
+
this.$l.maxIndex = pb.sub(this.size, pb.vec2(1));
|
|
654
|
+
this.$l.i0 = pb.ivec2(pb.clamp(this.i, pb.vec2(0), this.maxIndex));
|
|
655
|
+
this.$l.i1 = pb.ivec2(pb.clamp(pb.add(this.i, pb.vec2(1)), pb.vec2(0), this.maxIndex));
|
|
656
|
+
const texel = (scope, x, z)=>pb.textureLoad(scope.density, pb.ivec2(scope[x].x, scope[z].y), 0).r;
|
|
657
|
+
this.$l.a = pb.mix(texel(this, 'i0', 'i0'), texel(this, 'i1', 'i0'), this.f.x);
|
|
658
|
+
this.$l.b = pb.mix(texel(this, 'i0', 'i1'), texel(this, 'i1', 'i1'), this.f.x);
|
|
659
|
+
this.$return(pb.mix(this.a, this.b, this.f.y));
|
|
660
|
+
});
|
|
661
|
+
// (max, min) height over a rectangle of height map texels [lo, hi], read from the
|
|
662
|
+
// pyramid level where the rectangle spans at most 2x2 texels
|
|
663
|
+
pb.func('pyramidRange', [
|
|
664
|
+
pb.vec2('lo'),
|
|
665
|
+
pb.vec2('hi')
|
|
666
|
+
], function() {
|
|
667
|
+
this.$l.extent = pb.max(pb.sub(this.hi.x, this.lo.x), pb.sub(this.hi.y, this.lo.y));
|
|
668
|
+
this.$l.level = pb.clamp(pb.ceil(pb.log2(pb.max(this.extent, 1))), 0, pb.sub(this.heightInfo.z, 1));
|
|
669
|
+
this.$l.texelSize = pb.exp2(this.level);
|
|
670
|
+
this.$l.mip = pb.int(this.level);
|
|
671
|
+
this.$l.maxIndex = pb.sub(pb.vec2(pb.textureDimensions(this.heightPyramid, this.mip)), pb.vec2(1));
|
|
672
|
+
this.$l.i0 = pb.ivec2(pb.clamp(pb.floor(pb.div(this.lo, this.texelSize)), pb.vec2(0), this.maxIndex));
|
|
673
|
+
this.$l.i1 = pb.ivec2(pb.clamp(pb.floor(pb.div(this.hi, this.texelSize)), pb.vec2(0), this.maxIndex));
|
|
674
|
+
this.$l.a = pb.textureLoad(this.heightPyramid, this.i0, this.mip).rg;
|
|
675
|
+
this.$l.b = pb.textureLoad(this.heightPyramid, pb.ivec2(this.i1.x, this.i0.y), this.mip).rg;
|
|
676
|
+
this.$l.c = pb.textureLoad(this.heightPyramid, pb.ivec2(this.i0.x, this.i1.y), this.mip).rg;
|
|
677
|
+
this.$l.d = pb.textureLoad(this.heightPyramid, this.i1, this.mip).rg;
|
|
678
|
+
this.$return(pb.vec2(pb.max(pb.max(this.a.x, this.b.x), pb.max(this.c.x, this.d.x)), pb.min(pb.min(this.a.y, this.b.y), pb.min(this.c.y, this.d.y))));
|
|
679
|
+
});
|
|
680
|
+
pb.main(function() {
|
|
681
|
+
// Placement units of this thread's layer: cells of the near window, or 2x2 blocks of
|
|
682
|
+
// cells of the far window (z = 1, procedural blades only)
|
|
683
|
+
this.$l.far = pb.notEqual(this.$builtins.globalInvocationId.z, pb.uint(0));
|
|
684
|
+
this.$l.win = this.window;
|
|
685
|
+
this.$l.unit = pb.float(1);
|
|
686
|
+
if (blade) {
|
|
687
|
+
this.$if(this.far, function() {
|
|
688
|
+
this.win = this.window2;
|
|
689
|
+
this.unit = pb.float(2);
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
// Terrain occlusion, one test per workgroup: the group's blades are hidden if every
|
|
693
|
+
// line of sight to them dips below the terrain somewhere in front of them. Each
|
|
694
|
+
// thread checks one point along the way.
|
|
695
|
+
this.$l.occluded = pb.uint(0);
|
|
696
|
+
this.$if(pb.greaterThan(this.occlusion.x, 0.5), function() {
|
|
697
|
+
this.$l.cellSize = pb.div(pb.sub(this.region.zw, this.region.xy), this.cells.xy);
|
|
698
|
+
this.$l.toTexel = pb.div(this.heightInfo.xy, pb.sub(this.region.zw, this.region.xy));
|
|
699
|
+
this.$l.g0 = pb.mul(pb.add(this.win.xy, pb.mul(pb.vec2(this.$builtins.workGroupId.xy), WORKGROUP_SIZE)), this.unit);
|
|
700
|
+
this.$l.g1 = pb.min(pb.add(this.g0, pb.vec2(pb.mul(this.unit, WORKGROUP_SIZE))), pb.mul(pb.add(this.win.xy, this.win.zw), this.unit));
|
|
701
|
+
this.$l.fmin = pb.add(this.region.xy, pb.mul(this.g0, this.cellSize));
|
|
702
|
+
this.$l.fmax = pb.add(this.region.xy, pb.mul(this.g1, this.cellSize));
|
|
703
|
+
this.$l.cam = this.cameraPos.xz;
|
|
704
|
+
this.$l.camY = this.cameraPos.y;
|
|
705
|
+
// Highest blade tip of the group; one texel of margin for the bilinear heights
|
|
706
|
+
this.$l.groupRange = this.pyramidRange(pb.sub(pb.mul(pb.sub(this.fmin, this.region.xy), this.toTexel), pb.vec2(1)), pb.add(pb.mul(pb.sub(this.fmax, this.region.xy), this.toTexel), pb.vec2(1)));
|
|
707
|
+
this.$l.topY = pb.add(pb.add(pb.mul(this.groupRange.x, this.posScale.y), this.posScale.w), this.occlusion.w);
|
|
708
|
+
this.$l.dNear = pb.length(pb.max(pb.max(pb.sub(this.fmin, this.cam), pb.sub(this.cam, this.fmax)), pb.vec2(0)));
|
|
709
|
+
this.$l.dFar = pb.length(pb.max(pb.abs(pb.sub(this.cam, this.fmin)), pb.abs(pb.sub(this.cam, this.fmax))));
|
|
710
|
+
this.$l.center = pb.mul(pb.add(this.fmin, this.fmax), 0.5);
|
|
711
|
+
this.$l.groupRadius = pb.mul(pb.length(pb.sub(this.fmax, this.fmin)), 0.5);
|
|
712
|
+
this.$l.sMin = pb.mul(this.heightInfo.w, 2);
|
|
713
|
+
this.$l.sEnd = pb.sub(this.dNear, this.groupRadius);
|
|
714
|
+
// A camera below the terrain would see everything as occluded
|
|
715
|
+
this.$l.camTexel = pb.mul(pb.sub(this.cam, this.region.xy), this.toTexel);
|
|
716
|
+
this.$l.camGround = pb.add(pb.mul(this.pyramidRange(this.camTexel, this.camTexel).y, this.posScale.y), this.posScale.w);
|
|
717
|
+
this.$if(pb.and(pb.greaterThan(this.sEnd, this.sMin), pb.greaterThanEqual(this.camY, this.camGround)), function() {
|
|
718
|
+
// Steepest line of sight to the group: to the nearest edge when looking up at
|
|
719
|
+
// its highest tip, to the farthest one when looking down
|
|
720
|
+
this.$l.slope = pb.div(pb.sub(this.topY, this.camY), pb.mix(this.dFar, this.dNear, pb.step(this.camY, this.topY)));
|
|
721
|
+
this.$l.dir = pb.normalize(pb.sub(this.center, this.cam));
|
|
722
|
+
this.$l.k = pb.float(pb.add(pb.mul(this.$builtins.localInvocationId.y, WORKGROUP_SIZE), this.$builtins.localInvocationId.x));
|
|
723
|
+
this.$l.s = pb.add(this.sMin, pb.mul(pb.sub(this.sEnd, this.sMin), pb.div(pb.add(this.k, 0.5), OCCLUSION_SAMPLES)));
|
|
724
|
+
this.$l.p = pb.add(this.cam, pb.mul(this.dir, this.s));
|
|
725
|
+
// Lateral spread of the lines of sight at this distance, plus how far below the
|
|
726
|
+
// samples the rendered surface can reach: a clipmap level of 2^L cells averages
|
|
727
|
+
// heights within 4 * 2^L cells, and 2^L <= s / (T * cell) there, doubled for safety
|
|
728
|
+
this.$l.spread = pb.div(pb.mul(this.groupRadius, this.s), this.dNear);
|
|
729
|
+
this.$l.lodReach = pb.mul(pb.mul(pb.max(1, pb.div(pb.mul(this.s, 2), pb.mul(this.occlusion.y, this.heightInfo.w))), this.heightInfo.w), 4);
|
|
730
|
+
this.$l.halfSize = pb.mul(pb.vec2(pb.add(this.spread, this.lodReach)), this.toTexel);
|
|
731
|
+
this.$l.pc = pb.mul(pb.sub(this.p, this.region.xy), this.toTexel);
|
|
732
|
+
this.$l.lo = pb.sub(this.pc, this.halfSize);
|
|
733
|
+
this.$l.hi = pb.add(this.pc, this.halfSize);
|
|
734
|
+
// Beyond the terrain there is nothing to hide behind
|
|
735
|
+
this.$if(pb.and(pb.and(pb.greaterThanEqual(this.lo.x, 0), pb.greaterThanEqual(this.lo.y, 0)), pb.and(pb.lessThanEqual(this.hi.x, this.heightInfo.x), pb.lessThanEqual(this.hi.y, this.heightInfo.y))), function() {
|
|
736
|
+
this.$l.ground = pb.sub(pb.add(pb.mul(this.pyramidRange(this.lo, this.hi).y, this.posScale.y), this.posScale.w), this.occlusion.z);
|
|
737
|
+
this.$if(pb.lessThan(pb.add(this.camY, pb.mul(this.slope, this.s)), this.ground), function() {
|
|
738
|
+
pb.atomicOr(this.occludedFlag, 1);
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
});
|
|
742
|
+
pb.workgroupBarrier();
|
|
743
|
+
this.occluded = pb.atomicOr(this.occludedFlag, 0);
|
|
744
|
+
});
|
|
745
|
+
this.$l.id = this.$builtins.globalInvocationId.xy;
|
|
746
|
+
this.$if(pb.and(pb.and(pb.lessThan(this.id.x, pb.uint(this.win.z)), pb.lessThan(this.id.y, pb.uint(this.win.w))), pb.or(pb.equal(this.occluded, 0), pb.greaterThan(this.occlusion.x, 1.5))), function() {
|
|
747
|
+
this.$l.cx = pb.add(pb.uint(this.win.x), this.id.x);
|
|
748
|
+
this.$l.cz = pb.add(pb.uint(this.win.y), this.id.y);
|
|
749
|
+
this.$l.seed = pb.uint(this.params.x);
|
|
750
|
+
// Blades that give way to the low detail level: 1 for the three cells of each
|
|
751
|
+
// 2x2 block that the far layer does not keep
|
|
752
|
+
this.$l.extra = pb.float(0);
|
|
753
|
+
if (blade) {
|
|
754
|
+
this.$l.bseed = pb.add(pb.mul(this.seed, 16), pb.uint(BLADE_SEED_BASE));
|
|
755
|
+
// The cell of a 2x2 block the far layer keeps, picked by a hash of the block
|
|
756
|
+
this.$l.block = this.$choice(this.far, pb.uvec2(this.cx, this.cz), pb.uvec2(pb.sar(this.cx, 1), pb.sar(this.cz, 1)));
|
|
757
|
+
this.$l.kept = pb.min(pb.uint(pb.mul(this.grassHash(this.block.x, this.block.y, pb.add(this.bseed, 5)), 4)), pb.uint(3));
|
|
758
|
+
this.$if(this.far, function() {
|
|
759
|
+
this.cx = pb.add(pb.mul(this.block.x, 2), pb.compAnd(this.kept, pb.uint(1)));
|
|
760
|
+
this.cz = pb.add(pb.mul(this.block.y, 2), pb.sar(this.kept, 1));
|
|
761
|
+
}).$else(function() {
|
|
762
|
+
this.extra = pb.float(pb.notEqual(pb.add(pb.compAnd(this.cx, pb.uint(1)), pb.mul(pb.compAnd(this.cz, pb.uint(1)), 2)), this.kept));
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
this.$l.uv = pb.div(pb.add(pb.vec2(pb.float(this.cx), pb.float(this.cz)), pb.vec2(this.grassHash(this.cx, this.cz, this.seed), this.grassHash(this.cx, this.cz, pb.add(this.seed, 1)))), this.cells.xy);
|
|
766
|
+
this.$if(pb.and(pb.greaterThan(this.sampleDensity(this.uv), this.grassHash(this.cx, this.cz, pb.add(this.seed, 2))), // The cell a block of the far layer keeps can fall past the grid's last row
|
|
767
|
+
pb.and(pb.lessThan(this.cx, pb.uint(this.cells.x)), pb.lessThan(this.cz, pb.uint(this.cells.y)))), function() {
|
|
768
|
+
this.$l.regionSize = pb.sub(this.region.zw, this.region.xy);
|
|
769
|
+
if (blade) {
|
|
770
|
+
// Clumps, after "Procedural Grass in Ghost of Tsushima": a procedural
|
|
771
|
+
// Voronoi over the nearest 3x3 points of a grid, each jittered by a hash;
|
|
772
|
+
// the blade belongs to the clump of the nearest point
|
|
773
|
+
this.$l.cp = pb.mul(pb.add(this.region.xy, pb.mul(this.uv, this.regionSize)), this.clump0.x);
|
|
774
|
+
this.$l.cellBase = pb.floor(this.cp);
|
|
775
|
+
this.$l.bestDist = pb.float(1e9);
|
|
776
|
+
this.$l.clumpPoint = this.cp;
|
|
777
|
+
this.$l.clumpCell = pb.uvec2(0);
|
|
778
|
+
this.$l.cellF = pb.vec2(0);
|
|
779
|
+
this.$l.cellU = pb.uvec2(0);
|
|
780
|
+
this.$l.point = pb.vec2(0);
|
|
781
|
+
this.$l.pointDist = pb.float(0);
|
|
782
|
+
for(let dz = -1; dz <= 1; dz++){
|
|
783
|
+
for(let dx = -1; dx <= 1; dx++){
|
|
784
|
+
this.cellF = pb.add(this.cellBase, pb.vec2(dx, dz));
|
|
785
|
+
// Negative cells wrap to large unsigned values, all the hash needs
|
|
786
|
+
this.cellU = pb.uvec2(pb.ivec2(this.cellF));
|
|
787
|
+
this.point = pb.add(this.cellF, pb.vec2(this.grassHash(this.cellU.x, this.cellU.y, pb.add(this.bseed, 8)), this.grassHash(this.cellU.x, this.cellU.y, pb.add(this.bseed, 9))));
|
|
788
|
+
this.pointDist = pb.dot(pb.sub(this.point, this.cp), pb.sub(this.point, this.cp));
|
|
789
|
+
this.$if(pb.lessThan(this.pointDist, this.bestDist), function() {
|
|
790
|
+
this.bestDist = this.pointDist;
|
|
791
|
+
this.clumpPoint = this.point;
|
|
792
|
+
this.clumpCell = this.cellU;
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
this.$l.clumpCenter = pb.div(this.clumpPoint, this.clump0.x);
|
|
797
|
+
this.$l.clumpAngle = pb.mul(this.grassHash(this.clumpCell.x, this.clumpCell.y, pb.add(this.bseed, 10)), Math.PI * 2);
|
|
798
|
+
this.$l.clumpHeight = pb.sub(pb.mul(this.grassHash(this.clumpCell.x, this.clumpCell.y, pb.add(this.bseed, 11)), 2), 1);
|
|
799
|
+
this.$l.clumpColor = this.grassHash(this.clumpCell.x, this.clumpCell.y, pb.add(this.bseed, 12));
|
|
800
|
+
// Pull the blade toward its clump point before it is placed on the terrain
|
|
801
|
+
this.uv = pb.mix(this.uv, pb.div(pb.sub(this.clumpCenter, this.region.xy), this.regionSize), this.clump0.z);
|
|
802
|
+
}
|
|
803
|
+
this.$l.xz = pb.add(this.region.xy, pb.mul(this.uv, this.regionSize));
|
|
804
|
+
this.$l.height = pb.add(pb.mul(pb.textureSampleLevel(this.heightMap, this.uv, 0).r, this.posScale.y), this.posScale.w);
|
|
805
|
+
this.$l.base = pb.vec3(this.xz.x, this.height, this.xz.y);
|
|
806
|
+
this.$l.dist = pb.distance(this.base, this.cameraPos.xyz);
|
|
807
|
+
this.$l.visible = pb.or(pb.lessThanEqual(this.params.y, 0), pb.lessThanEqual(this.dist, this.params.y));
|
|
808
|
+
this.$l.lodHash = this.grassHash(this.cx, this.cz, pb.add(this.seed, 4));
|
|
809
|
+
if (blade) {
|
|
810
|
+
// Detail level: each blade belongs to the near layer up to the low detail
|
|
811
|
+
// distance and to the far layer from there on, measured the same way in both
|
|
812
|
+
this.visible = pb.and(this.visible, pb.equal(this.far, pb.greaterThanEqual(this.dist, this.lod.x)));
|
|
813
|
+
} else {
|
|
814
|
+
// Density LOD, see ClipmapGrassMaterial: drop the blades the material would
|
|
815
|
+
// shrink to nothing
|
|
816
|
+
this.$if(pb.greaterThan(this.params.y, 0), function() {
|
|
817
|
+
this.$l.keep = pb.mix(1, this.densityLod.y, pb.smoothStep(this.densityLod.x, this.params.y, this.dist));
|
|
818
|
+
this.visible = pb.and(this.visible, pb.lessThan(pb.mul(this.lodHash, 1 - GRASS_LOD_FADE_BAND), this.keep));
|
|
819
|
+
});
|
|
820
|
+
}
|
|
821
|
+
if (blade) {
|
|
822
|
+
// Shadow caster fade: thin out to none over a band of view depth, each blade
|
|
823
|
+
// leaving at its own depth like the density LOD
|
|
824
|
+
this.$if(pb.greaterThan(this.lod.w, 0), function() {
|
|
825
|
+
this.$l.viewZ = pb.add(pb.dot(this.viewDepth.xyz, this.base), this.viewDepth.w);
|
|
826
|
+
this.$l.fadeKeep = pb.sub(1, pb.clamp(pb.mul(pb.sub(this.viewZ, this.lod.z), this.lod.w), 0, 1));
|
|
827
|
+
this.visible = pb.and(this.visible, pb.lessThan(this.grassHash(this.cx, this.cz, pb.add(this.bseed, 13)), this.fadeKeep));
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
this.$l.center = pb.add(this.base, pb.vec3(0, this.params.w, 0));
|
|
831
|
+
for(let i = 0; i < GRASS_CULL_PLANES; i++){
|
|
832
|
+
this.visible = pb.and(this.visible, pb.greaterThanEqual(pb.add(pb.dot(this.planes.at(i).xyz, this.center), this.planes.at(i).w), pb.neg(this.params.z)));
|
|
833
|
+
}
|
|
834
|
+
this.$if(this.visible, function() {
|
|
835
|
+
this.$l.angle = pb.mul(this.grassHash(this.cx, this.cz, pb.add(this.seed, 3)), Math.PI * 2);
|
|
836
|
+
// Occluded blades kept for debugging are flagged by adding 2 to the hash; blade
|
|
837
|
+
// instances carry the extra flag in its place, their density LOD is the
|
|
838
|
+
// detail levels
|
|
839
|
+
this.$l.flaggedHash = pb.add(blade ? this.extra : this.lodHash, pb.mul(pb.float(pb.notEqual(this.occluded, 0)), 2));
|
|
840
|
+
if (blade) {
|
|
841
|
+
// Near blades append to the high detail list, far ones to the low detail
|
|
842
|
+
// list, whose draw arguments follow and whose instances start at lod.y
|
|
843
|
+
this.$l.slot = pb.uint(0);
|
|
844
|
+
this.$if(this.far, function() {
|
|
845
|
+
this.slot = pb.add(pb.atomicAdd(this.args.at(6), 1), pb.uint(this.lod.y));
|
|
846
|
+
}).$else(function() {
|
|
847
|
+
this.slot = pb.atomicAdd(this.args.at(1), 1);
|
|
848
|
+
});
|
|
849
|
+
} else {
|
|
850
|
+
this.$l.slot = pb.atomicAdd(this.args.at(1), 1);
|
|
851
|
+
}
|
|
852
|
+
if (!blade) {
|
|
853
|
+
this.instances.setAt(this.slot, pb.vec4(this.uv, this.angle, this.flaggedHash));
|
|
854
|
+
} else {
|
|
855
|
+
// Layout: see ClipmapBladeGrassMaterial
|
|
856
|
+
const signedHash = (scope, k)=>pb.sub(pb.mul(scope.grassHash(scope.cx, scope.cz, pb.add(scope.bseed, k)), 2), 1);
|
|
857
|
+
this.$l.bladeHeight = pb.mul(this.shape0.x, pb.max(0, pb.add(1, pb.mul(this.shape0.y, signedHash(this, 0)))), pb.max(0, pb.add(1, pb.mul(this.clump0.y, this.clumpHeight))));
|
|
858
|
+
this.$l.bladeWidth = pb.mul(this.shape0.z, pb.max(0, pb.add(1, pb.mul(this.shape0.w, signedHash(this, 1)))));
|
|
859
|
+
this.$l.tilt = pb.clamp(pb.add(this.shape1.x, pb.mul(this.shape1.y, signedHash(this, 2))), 0, 1);
|
|
860
|
+
this.$l.bend = pb.add(this.shape1.z, pb.mul(this.shape1.w, signedHash(this, 3)));
|
|
861
|
+
this.$l.bladeHash = this.grassHash(this.cx, this.cz, pb.add(this.bseed, 4));
|
|
862
|
+
// Facing: toward the clump's shared direction, then away from its point
|
|
863
|
+
this.$l.clumpDir = pb.vec2(pb.cos(this.clumpAngle), pb.sin(this.clumpAngle));
|
|
864
|
+
this.$l.facing = pb.mix(pb.vec2(pb.cos(this.angle), pb.sin(this.angle)), this.clumpDir, this.clump0.w);
|
|
865
|
+
this.$l.away = pb.sub(this.xz, this.clumpCenter);
|
|
866
|
+
this.$l.awayLen = pb.length(this.away);
|
|
867
|
+
this.$if(pb.greaterThan(this.awayLen, 1e-5), function() {
|
|
868
|
+
this.facing = pb.mix(this.facing, pb.div(this.away, this.awayLen), this.clump1.x);
|
|
869
|
+
});
|
|
870
|
+
this.$if(pb.lessThan(pb.dot(this.facing, this.facing), 1e-8), function() {
|
|
871
|
+
this.facing = this.clumpDir;
|
|
872
|
+
});
|
|
873
|
+
this.$l.facingAngle = pb.atan2(this.facing.y, this.facing.x);
|
|
874
|
+
// Wind: sample the push for this frame and the last one, and turn the facing
|
|
875
|
+
// downwind by it; the vertex shader rebuilds both frames for motion vectors
|
|
876
|
+
this.$l.windPush = WindField.shaderPush(this, this.xz, this.wind.at(0), this.wind.at(1), this.wind.at(2), this.wind.at(3));
|
|
877
|
+
this.$l.windPushPrev = WindField.shaderPush(this, this.xz, this.wind.at(4), this.wind.at(5), this.wind.at(6), this.wind.at(7));
|
|
878
|
+
this.$l.facingNow = this.facingAngle;
|
|
879
|
+
this.$l.facingPrev = this.facingAngle;
|
|
880
|
+
this.$l.bladeDir = pb.vec2(pb.cos(this.facingAngle), pb.sin(this.facingAngle));
|
|
881
|
+
const turn = (scope, out, push, dir)=>{
|
|
882
|
+
scope.$if(pb.and(pb.greaterThan(scope[push], 0), pb.greaterThan(pb.dot(dir, dir), 1e-6)), function() {
|
|
883
|
+
this[out] = pb.add(this.facingAngle, pb.mul(pb.atan2(pb.sub(pb.mul(this.bladeDir.x, dir.y), pb.mul(this.bladeDir.y, dir.x)), pb.dot(this.bladeDir, dir)), pb.clamp(pb.mul(this[push], this.clump1.y), 0, 1)));
|
|
884
|
+
});
|
|
885
|
+
};
|
|
886
|
+
turn(this, 'facingNow', 'windPush', this.wind.at(0).xy);
|
|
887
|
+
turn(this, 'facingPrev', 'windPushPrev', this.wind.at(4).xy);
|
|
888
|
+
// Terrain normal under the blade (central differences of the height map, as
|
|
889
|
+
// in ClipmapGrassMaterial), x and z quantized to 12 bits each and packed into
|
|
890
|
+
// an integer a float holds exactly. Distant blades shade toward it.
|
|
891
|
+
this.$l.texel = pb.div(pb.vec2(1), this.heightInfo.xy);
|
|
892
|
+
this.$l.sampleDist = pb.mul(this.regionSize, pb.mul(this.texel, 2));
|
|
893
|
+
this.$l.hL = pb.textureSampleLevel(this.heightMap, pb.sub(this.uv, pb.vec2(this.texel.x, 0)), 0).r;
|
|
894
|
+
this.$l.hR = pb.textureSampleLevel(this.heightMap, pb.add(this.uv, pb.vec2(this.texel.x, 0)), 0).r;
|
|
895
|
+
this.$l.hU = pb.textureSampleLevel(this.heightMap, pb.sub(this.uv, pb.vec2(0, this.texel.y)), 0).r;
|
|
896
|
+
this.$l.hD = pb.textureSampleLevel(this.heightMap, pb.add(this.uv, pb.vec2(0, this.texel.y)), 0).r;
|
|
897
|
+
this.$l.terrainNormal = pb.normalize(pb.vec3(pb.neg(pb.div(pb.mul(pb.sub(this.hR, this.hL), this.posScale.y), this.sampleDist.x)), 1, pb.neg(pb.div(pb.mul(pb.sub(this.hD, this.hU), this.posScale.y), this.sampleDist.y))));
|
|
898
|
+
this.$l.quantized = pb.floor(pb.add(pb.mul(pb.add(pb.mul(this.terrainNormal.xz, 0.5), pb.vec2(0.5)), 4095), pb.vec2(0.5)));
|
|
899
|
+
this.$l.packedNormal = pb.add(pb.mul(this.quantized.x, 4096), this.quantized.y);
|
|
900
|
+
this.$l.first = pb.mul(this.slot, INSTANCE_VEC4.blade);
|
|
901
|
+
this.instances.setAt(this.first, pb.vec4(this.base, this.flaggedHash));
|
|
902
|
+
this.instances.setAt(pb.add(this.first, 1), pb.vec4(this.facingNow, this.facingPrev, this.windPush, this.windPushPrev));
|
|
903
|
+
this.instances.setAt(pb.add(this.first, 2), pb.vec4(this.clumpAngle, this.clumpColor, this.packedNormal, this.bladeHash));
|
|
904
|
+
this.instances.setAt(pb.add(this.first, 3), pb.vec4(this.bladeHeight, this.bladeWidth, this.tilt, this.bend));
|
|
905
|
+
}
|
|
906
|
+
});
|
|
907
|
+
});
|
|
908
|
+
});
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
this._programs[kind] = program;
|
|
913
|
+
}
|
|
914
|
+
return program;
|
|
915
|
+
}
|
|
916
|
+
onDispose() {
|
|
917
|
+
super.onDispose();
|
|
918
|
+
this._densityTexture.dispose();
|
|
919
|
+
for (const target of this._targets){
|
|
920
|
+
GrassGpuPlacement.disposeTarget(target);
|
|
921
|
+
}
|
|
922
|
+
this._targets.length = 0;
|
|
923
|
+
this._baseVertexBuffer.dispose();
|
|
924
|
+
this._indexBuffer.dispose();
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export { GRASS_BLADE_LOD_BAND, GRASS_CULL_PLANES, GRASS_LOD_FADE_BAND, GrassGpuPlacement, GrassOcclusionMode, grassBladeLodDistance, grassDensityLod, grassHash };
|
|
929
|
+
//# sourceMappingURL=grass_gpu.js.map
|