@zephyr3d/scene 0.9.27 → 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 +54 -0
- 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 +279 -55
- 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 +171 -1
- 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 +104 -178
- 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 +25436 -15990
- package/dist/index.js +65 -11
- 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/lambert.js +4 -1
- package/dist/material/lambert.js.map +1 -1
- package/dist/material/material.js +66 -4
- 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 +191 -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 +333 -191
- 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 +1261 -1438
- 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 +106 -106
- package/dist/posteffect/motionblur.js.map +1 -1
- package/dist/posteffect/postsss.js +572 -0
- package/dist/posteffect/postsss.js.map +1 -0
- package/dist/posteffect/sao.js +2 -6
- 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 +4 -13
- 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 +3 -2
- package/dist/render/abuffer_oit.js.map +1 -1
- package/dist/render/clipmap.js +179 -64
- 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 +25 -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 +5 -5
- 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 +5 -10
- package/dist/render/fbm_wavegenerator.js.map +1 -1
- package/dist/render/fft_wavegenerator.js +247 -157
- package/dist/render/fft_wavegenerator.js.map +1 -1
- package/dist/render/gerstner_wavegenerator.js +16 -38
- 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/instance_culling.js +499 -0
- package/dist/render/instance_culling.js.map +1 -0
- package/dist/render/lightpass.js +19 -8
- 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/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 +162 -11
- package/dist/render/render_queue.js.map +1 -1
- package/dist/render/renderer.js +10 -7
- package/dist/render/renderer.js.map +1 -1
- package/dist/render/rendergraph/blackboard.js +14 -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 +272 -55
- package/dist/render/rendergraph/forward_plus_builder.js.map +1 -1
- package/dist/render/rendergraph/frame_graph_context.js +41 -0
- package/dist/render/rendergraph/frame_graph_context.js.map +1 -0
- 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 +58 -158
- package/dist/render/underwater.js.map +1 -1
- 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 +134 -319
- package/dist/render/water_caustics.js.map +1 -1
- 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 +3 -3
- 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 +633 -268
- 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/ssgi.js +63 -0
- package/dist/shaders/ssgi.js.map +1 -0
- package/dist/shaders/ssgi_reprojection.js +118 -0
- package/dist/shaders/ssgi_reprojection.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 +32 -82
- package/dist/shaders/water.js.map +1 -1
- package/dist/shaders/water_medium.js +7 -21
- package/dist/shaders/water_medium.js.map +1 -1
- 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 +40 -2
- 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 +34 -6
- package/dist/utility/serialization/manager.js.map +1 -1
- package/dist/utility/serialization/scene/animation.js +46 -5
- 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 +87 -254
- 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 +1543 -264
- package/dist/utility/serialization/scene/water.js.map +1 -1
- package/dist/utility/serialization/types.js.map +1 -1
- package/dist/utility/shprojector.js +10 -22
- 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
- package/dist/animation/joint_dynamics/convex_collider.js +0 -320
- package/dist/animation/joint_dynamics/convex_collider.js.map +0 -1
|
@@ -0,0 +1,1020 @@
|
|
|
1
|
+
import { Quaternion, Vector3, Matrix4x4, AABB, DRef } from '@zephyr3d/base';
|
|
2
|
+
import { SceneNode } from './scene_node.js';
|
|
3
|
+
import { GraphNode } from './graph_node.js';
|
|
4
|
+
import { BatchGroup } from './batchgroup.js';
|
|
5
|
+
import { Mesh } from './mesh.js';
|
|
6
|
+
import { getEngine } from '../app/api.js';
|
|
7
|
+
import '../physics/part.js';
|
|
8
|
+
import { NodePhysics } from '../physics/node_physics.js';
|
|
9
|
+
import { RigidBody } from '../physics/rigid_body.js';
|
|
10
|
+
import { Collider } from '../physics/collider.js';
|
|
11
|
+
import { FoliageInstanceBuckets, FOLIAGE_INSTANCE_STRIDE } from './foliage_buckets.js';
|
|
12
|
+
import { FoliageDensityMask } from './foliage_mask.js';
|
|
13
|
+
export { FOLIAGE_MASK_TILE_SIZE, foliageBrushWeight } from './foliage_mask.js';
|
|
14
|
+
|
|
15
|
+
function defaultGeneration() {
|
|
16
|
+
return {
|
|
17
|
+
spacing: 4,
|
|
18
|
+
scaleRange: [
|
|
19
|
+
0.8,
|
|
20
|
+
1.2
|
|
21
|
+
],
|
|
22
|
+
slopeRange: [
|
|
23
|
+
0,
|
|
24
|
+
45
|
|
25
|
+
],
|
|
26
|
+
heightRange: null,
|
|
27
|
+
yOffset: 0,
|
|
28
|
+
randomYaw: true,
|
|
29
|
+
seed: Math.floor(Math.random() * 0x7fffffff),
|
|
30
|
+
surface: 'terrain',
|
|
31
|
+
placeScript: ''
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function copyGeneration(g) {
|
|
35
|
+
return {
|
|
36
|
+
...g,
|
|
37
|
+
scaleRange: [
|
|
38
|
+
g.scaleRange[0],
|
|
39
|
+
g.scaleRange[1]
|
|
40
|
+
],
|
|
41
|
+
slopeRange: [
|
|
42
|
+
g.slopeRange[0],
|
|
43
|
+
g.slopeRange[1]
|
|
44
|
+
],
|
|
45
|
+
heightRange: g.heightRange ? [
|
|
46
|
+
g.heightRange[0],
|
|
47
|
+
g.heightRange[1]
|
|
48
|
+
] : null
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** A generation setting with the omitted and invalid fields of `g` taken from `base` */ function mergeGeneration(base, g) {
|
|
52
|
+
const num = (v, d)=>typeof v === 'number' && Number.isFinite(v) ? v : d;
|
|
53
|
+
const pair = (v, d)=>Array.isArray(v) && v.length === 2 ? [
|
|
54
|
+
num(v[0], d[0]),
|
|
55
|
+
num(v[1], d[1])
|
|
56
|
+
] : [
|
|
57
|
+
d[0],
|
|
58
|
+
d[1]
|
|
59
|
+
];
|
|
60
|
+
return {
|
|
61
|
+
spacing: Math.max(0.05, num(g.spacing, base.spacing)),
|
|
62
|
+
scaleRange: pair(g.scaleRange, base.scaleRange),
|
|
63
|
+
slopeRange: pair(g.slopeRange, base.slopeRange),
|
|
64
|
+
heightRange: g.heightRange === undefined ? base.heightRange ? [
|
|
65
|
+
base.heightRange[0],
|
|
66
|
+
base.heightRange[1]
|
|
67
|
+
] : null : g.heightRange ? pair(g.heightRange, [
|
|
68
|
+
-Infinity,
|
|
69
|
+
Infinity
|
|
70
|
+
]) : null,
|
|
71
|
+
yOffset: num(g.yOffset, base.yOffset),
|
|
72
|
+
randomYaw: typeof g.randomYaw === 'boolean' ? g.randomYaw : base.randomYaw,
|
|
73
|
+
seed: Math.floor(num(g.seed, base.seed)) | 0,
|
|
74
|
+
surface: g.surface === 'terrain' || g.surface === 'any' ? g.surface : base.surface,
|
|
75
|
+
placeScript: typeof g.placeScript === 'string' ? g.placeScript : base.placeScript
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const PRIMITIVE_COLLIDER_SHAPES = [
|
|
79
|
+
'box',
|
|
80
|
+
'sphere',
|
|
81
|
+
'capsule',
|
|
82
|
+
'cylinder'
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* One kind of object scattered by a {@link FoliageSystem}: an asset and where its copies stand.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* A layer holds two groups of instances. The generated ones follow from its
|
|
89
|
+
* {@link FoliageLayer.mask | density mask} and {@link FoliageLayer.generation | generation
|
|
90
|
+
* settings}, and only painting and regenerating change them. The manual ones are placed
|
|
91
|
+
* directly and are left alone by generation; only erasing and the instance methods remove them.
|
|
92
|
+
* Both are drawn and collide alike.
|
|
93
|
+
* @public
|
|
94
|
+
*/ class FoliageLayer {
|
|
95
|
+
/** The foliage system the layer belongs to @internal */ _owner;
|
|
96
|
+
/** @internal */ _asset;
|
|
97
|
+
/** @internal */ _generated;
|
|
98
|
+
/** @internal */ _manual;
|
|
99
|
+
/** @internal */ _mask;
|
|
100
|
+
/** @internal */ _generation;
|
|
101
|
+
/** @internal */ _castShadow;
|
|
102
|
+
/** @internal */ _cullDistance;
|
|
103
|
+
/** @internal */ _alignToNormal;
|
|
104
|
+
/** @internal */ _collision;
|
|
105
|
+
/** @internal */ _parts;
|
|
106
|
+
/** Bounds of the asset's meshes, relative to its root @internal */ _bounds;
|
|
107
|
+
/** Colliders found on the asset's nodes @internal */ _assetColliders;
|
|
108
|
+
/** @internal */ _loading;
|
|
109
|
+
/**
|
|
110
|
+
* Creates a layer; add it to a foliage system with {@link FoliageSystem.insertLayer}
|
|
111
|
+
* @param asset - Path of the prefab or model asset the layer scatters
|
|
112
|
+
* @param settings - Layer settings
|
|
113
|
+
*/ constructor(asset = '', settings){
|
|
114
|
+
this._owner = null;
|
|
115
|
+
this._asset = asset;
|
|
116
|
+
this._generated = new FoliageInstanceBuckets(64);
|
|
117
|
+
this._manual = new FoliageInstanceBuckets(64);
|
|
118
|
+
this._mask = new FoliageDensityMask(1);
|
|
119
|
+
this._generation = mergeGeneration(defaultGeneration(), settings?.generation ?? {});
|
|
120
|
+
this._castShadow = settings?.castShadow ?? true;
|
|
121
|
+
this._cullDistance = settings?.cullDistance ?? 300;
|
|
122
|
+
this._alignToNormal = settings?.alignToNormal ?? 0;
|
|
123
|
+
this._collision = copyCollision(settings?.collision);
|
|
124
|
+
this._parts = null;
|
|
125
|
+
this._bounds = null;
|
|
126
|
+
this._assetColliders = [];
|
|
127
|
+
this._loading = null;
|
|
128
|
+
}
|
|
129
|
+
/** The foliage system the layer belongs to */ get owner() {
|
|
130
|
+
return this._owner;
|
|
131
|
+
}
|
|
132
|
+
/** Path of the prefab or model asset the layer scatters; changing it keeps the instances */ get asset() {
|
|
133
|
+
return this._asset;
|
|
134
|
+
}
|
|
135
|
+
set asset(val) {
|
|
136
|
+
if (val !== this._asset) {
|
|
137
|
+
this._dispose();
|
|
138
|
+
this._asset = val;
|
|
139
|
+
this._owner?.invalidate();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Number of instances of both groups */ get count() {
|
|
143
|
+
return this._generated.count + this._manual.count;
|
|
144
|
+
}
|
|
145
|
+
/** Number of instances generated from the density mask */ get generatedCount() {
|
|
146
|
+
return this._generated.count;
|
|
147
|
+
}
|
|
148
|
+
/** Number of instances placed directly */ get manualCount() {
|
|
149
|
+
return this._manual.count;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Where the layer grows when painted. Change it through the foliage brush or
|
|
153
|
+
* {@link FoliageSystem.paintMask}, which keep the generated instances in step.
|
|
154
|
+
*/ get mask() {
|
|
155
|
+
return this._mask;
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* How painted instances are generated. Reading gives a copy; assigning one changes the
|
|
159
|
+
* settings but not the instances, until the layer is regenerated.
|
|
160
|
+
*/ get generation() {
|
|
161
|
+
return copyGeneration(this._generation);
|
|
162
|
+
}
|
|
163
|
+
set generation(val) {
|
|
164
|
+
this._generation = mergeGeneration(this._generation, val);
|
|
165
|
+
}
|
|
166
|
+
/** Whether the instances cast shadows */ get castShadow() {
|
|
167
|
+
return this._castShadow;
|
|
168
|
+
}
|
|
169
|
+
set castShadow(val) {
|
|
170
|
+
if (val !== this._castShadow) {
|
|
171
|
+
this._castShadow = val;
|
|
172
|
+
this._owner?.invalidate();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/** Distance from the camera beyond which the instances are not drawn */ get cullDistance() {
|
|
176
|
+
return this._cullDistance;
|
|
177
|
+
}
|
|
178
|
+
set cullDistance(val) {
|
|
179
|
+
// Only decides which chunks are shown each frame, nothing to rebuild
|
|
180
|
+
this._cullDistance = val;
|
|
181
|
+
}
|
|
182
|
+
/** How much the instances lean with the ground: 0 stays upright, 1 follows the normal */ get alignToNormal() {
|
|
183
|
+
return this._alignToNormal;
|
|
184
|
+
}
|
|
185
|
+
set alignToNormal(val) {
|
|
186
|
+
if (val !== this._alignToNormal) {
|
|
187
|
+
this._alignToNormal = val;
|
|
188
|
+
this._owner?.invalidate();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/** What the instances collide with; null for nothing. Reading gives a copy; assign to change it. */ get collision() {
|
|
192
|
+
return copyCollision(this._collision);
|
|
193
|
+
}
|
|
194
|
+
set collision(val) {
|
|
195
|
+
this._collision = copyCollision(val);
|
|
196
|
+
this._owner?.invalidate();
|
|
197
|
+
}
|
|
198
|
+
/** @internal */ _colliderTemplates() {
|
|
199
|
+
const c = this._collision;
|
|
200
|
+
if (!c) {
|
|
201
|
+
return [];
|
|
202
|
+
}
|
|
203
|
+
if (c.shape === 'asset') {
|
|
204
|
+
return this._assetColliders;
|
|
205
|
+
}
|
|
206
|
+
const b = this._bounds;
|
|
207
|
+
const ext = b ? Vector3.sub(b.maxPoint, b.minPoint) : new Vector3(1, 1, 1);
|
|
208
|
+
const center = b ? Vector3.scale(Vector3.add(b.minPoint, b.maxPoint), 0.5) : new Vector3(0, 0.5, 0);
|
|
209
|
+
const bottom = b ? b.minPoint.y : 0;
|
|
210
|
+
const height = c.height ?? ext.y;
|
|
211
|
+
const radius = c.radius ?? (c.shape === 'sphere' ? Math.max(ext.x, ext.y, ext.z) : Math.min(ext.x, ext.z)) / 2;
|
|
212
|
+
const offset = c.offset ? new Vector3(c.offset[0], c.offset[1], c.offset[2]) : c.shape === 'capsule' || c.shape === 'cylinder' ? new Vector3(0, bottom + height / 2, 0) : center;
|
|
213
|
+
return [
|
|
214
|
+
{
|
|
215
|
+
matrix: Matrix4x4.identity(),
|
|
216
|
+
shape: c.shape,
|
|
217
|
+
size: c.size ? new Vector3(c.size[0], c.size[1], c.size[2]) : ext,
|
|
218
|
+
radius,
|
|
219
|
+
height,
|
|
220
|
+
offset,
|
|
221
|
+
friction: c.friction ?? 0.5,
|
|
222
|
+
restitution: c.restitution ?? 0,
|
|
223
|
+
layer: c.layer ?? 0
|
|
224
|
+
}
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* The instances, {@link FOLIAGE_INSTANCE_STRIDE} floats each: x, z (local to the foliage system),
|
|
229
|
+
* height offset above the ground, rotation about the vertical axis in radians, uniform scale.
|
|
230
|
+
* A copy: change instances with the methods of {@link FoliageSystem}.
|
|
231
|
+
* @param group - Which instances; both groups by default, the manual ones first
|
|
232
|
+
*/ getInstances(group = 'all') {
|
|
233
|
+
if (group === 'generated') {
|
|
234
|
+
return this._generated.toArray();
|
|
235
|
+
}
|
|
236
|
+
if (group === 'manual') {
|
|
237
|
+
return this._manual.toArray();
|
|
238
|
+
}
|
|
239
|
+
const manual = this._manual.toArray();
|
|
240
|
+
const generated = this._generated.toArray();
|
|
241
|
+
const out = new Float32Array(manual.length + generated.length);
|
|
242
|
+
out.set(manual);
|
|
243
|
+
out.set(generated, manual.length);
|
|
244
|
+
return out;
|
|
245
|
+
}
|
|
246
|
+
/** @internal */ _dispose() {
|
|
247
|
+
for (const part of this._parts ?? []){
|
|
248
|
+
part.primitive.dispose();
|
|
249
|
+
part.material.dispose();
|
|
250
|
+
}
|
|
251
|
+
this._parts = null;
|
|
252
|
+
this._bounds = null;
|
|
253
|
+
this._assetColliders = [];
|
|
254
|
+
// A load still running is for the old asset, it discards its result
|
|
255
|
+
this._loading = null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const COLLISION_SHAPES = [
|
|
259
|
+
'box',
|
|
260
|
+
'sphere',
|
|
261
|
+
'capsule',
|
|
262
|
+
'cylinder',
|
|
263
|
+
'asset'
|
|
264
|
+
];
|
|
265
|
+
/** A copy of a collision setting; null for none, or for one without a valid shape */ function copyCollision(c) {
|
|
266
|
+
if (!c || !COLLISION_SHAPES.includes(c.shape)) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
...c,
|
|
271
|
+
size: c.size ? [
|
|
272
|
+
c.size[0],
|
|
273
|
+
c.size[1],
|
|
274
|
+
c.size[2]
|
|
275
|
+
] : undefined,
|
|
276
|
+
offset: c.offset ? [
|
|
277
|
+
c.offset[0],
|
|
278
|
+
c.offset[1],
|
|
279
|
+
c.offset[2]
|
|
280
|
+
] : undefined
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
/** Deterministic value in [0, 1) from a position, standing in for the threshold of a manual instance */ function positionHash(x, z) {
|
|
284
|
+
const f = new Float32Array([
|
|
285
|
+
x,
|
|
286
|
+
z
|
|
287
|
+
]);
|
|
288
|
+
const u = new Uint32Array(f.buffer);
|
|
289
|
+
let h = Math.imul(u[0] ^ 0x9e3779b9, 0x85ebca6b) ^ Math.imul(u[1] ^ 0x7f4a7c15, 0xc2b2ae35);
|
|
290
|
+
h ^= h >>> 16;
|
|
291
|
+
h = Math.imul(h, 0x7feb352d);
|
|
292
|
+
h ^= h >>> 15;
|
|
293
|
+
h = Math.imul(h, 0x846ca68b);
|
|
294
|
+
h ^= h >>> 16;
|
|
295
|
+
return (h >>> 0) / 4294967296;
|
|
296
|
+
}
|
|
297
|
+
const tmpQuat = new Quaternion();
|
|
298
|
+
const tmpAlign = new Quaternion();
|
|
299
|
+
const tmpNormal = new Vector3();
|
|
300
|
+
const tmpScale = new Vector3();
|
|
301
|
+
const tmpPos = new Vector3();
|
|
302
|
+
const tmpMatrix = new Matrix4x4();
|
|
303
|
+
const tmpMatrix2 = new Matrix4x4();
|
|
304
|
+
const UP = Vector3.axisPY();
|
|
305
|
+
/**
|
|
306
|
+
* Scatters many copies of prefabs or models, such as trees, bushes and rocks, over a terrain.
|
|
307
|
+
*
|
|
308
|
+
* @remarks
|
|
309
|
+
* Each {@link FoliageLayer} scatters one asset. An instance stores only its horizontal position,
|
|
310
|
+
* an offset above the ground, a rotation about the vertical axis and a scale; its height is taken
|
|
311
|
+
* from the {@link ClipmapTerrain} the system is a descendant of, so the instances follow the
|
|
312
|
+
* ground when the terrain changes. Without a terrain ancestor the offset is the height.
|
|
313
|
+
*
|
|
314
|
+
* Instances are drawn in square chunks of {@link FoliageSystem.chunkSize}, each a
|
|
315
|
+
* {@link BatchGroup} built on demand and not serialized, which draws a whole chunk in a few
|
|
316
|
+
* instanced draw calls; chunks farther than the cull distance of their layer are hidden.
|
|
317
|
+
* Changing the instances of an area rebuilds only the chunks it overlaps.
|
|
318
|
+
*
|
|
319
|
+
* Positions are in the space of the foliage system, which is meant to be translated only.
|
|
320
|
+
* @public
|
|
321
|
+
*/ class FoliageSystem extends GraphNode {
|
|
322
|
+
_layers;
|
|
323
|
+
_chunkSize;
|
|
324
|
+
_maskCellSize;
|
|
325
|
+
_chunks;
|
|
326
|
+
_dirty;
|
|
327
|
+
_dirtyChunks;
|
|
328
|
+
_buildSerial;
|
|
329
|
+
_builtHeightData;
|
|
330
|
+
_builtTerrain;
|
|
331
|
+
/**
|
|
332
|
+
* Creates a foliage system
|
|
333
|
+
* @param scene - The scene to which the node belongs
|
|
334
|
+
*/ constructor(scene){
|
|
335
|
+
super(scene);
|
|
336
|
+
this._layers = [];
|
|
337
|
+
this._chunkSize = 64;
|
|
338
|
+
this._maskCellSize = 1;
|
|
339
|
+
this._chunks = new Map();
|
|
340
|
+
this._dirty = false;
|
|
341
|
+
this._dirtyChunks = new Map();
|
|
342
|
+
this._buildSerial = 0;
|
|
343
|
+
this._builtHeightData = null;
|
|
344
|
+
this._builtTerrain = null;
|
|
345
|
+
}
|
|
346
|
+
/** Edge length of the square chunks the instances are drawn in. Default 64. */ get chunkSize() {
|
|
347
|
+
return this._chunkSize;
|
|
348
|
+
}
|
|
349
|
+
set chunkSize(val) {
|
|
350
|
+
val = Math.max(1, val);
|
|
351
|
+
if (val !== this._chunkSize) {
|
|
352
|
+
this._chunkSize = val;
|
|
353
|
+
for (const layer of this._layers){
|
|
354
|
+
layer._generated.setChunkSize(val);
|
|
355
|
+
layer._manual.setChunkSize(val);
|
|
356
|
+
}
|
|
357
|
+
this.invalidate();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Edge of a cell of the density masks of the layers, in meters. Default 1. Changing it keeps
|
|
362
|
+
* the cell values, so what was painted scales with it; regenerate the layers afterwards.
|
|
363
|
+
*/ get maskCellSize() {
|
|
364
|
+
return this._maskCellSize;
|
|
365
|
+
}
|
|
366
|
+
set maskCellSize(val) {
|
|
367
|
+
val = Math.max(0.05, val);
|
|
368
|
+
if (val !== this._maskCellSize) {
|
|
369
|
+
this._maskCellSize = val;
|
|
370
|
+
for (const layer of this._layers){
|
|
371
|
+
layer._mask.cellSize = val;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
/** Number of layers */ get numLayers() {
|
|
376
|
+
return this._layers.length;
|
|
377
|
+
}
|
|
378
|
+
/** Total number of instances of all layers */ get numInstances() {
|
|
379
|
+
return this._layers.reduce((sum, layer)=>sum + layer.count, 0);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Gets a layer
|
|
383
|
+
* @param index - Layer index
|
|
384
|
+
*/ getLayer(index) {
|
|
385
|
+
return this._layers[index] ?? null;
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Index of a layer of this system
|
|
389
|
+
* @param layer - The layer
|
|
390
|
+
* @returns Its index, or -1
|
|
391
|
+
*/ indexOfLayer(layer) {
|
|
392
|
+
return this._layers.indexOf(layer);
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Adds a layer scattering a prefab (.zprefab) or a model asset
|
|
396
|
+
* @param asset - Path of the asset
|
|
397
|
+
* @param settings - Layer settings
|
|
398
|
+
* @returns Index of the new layer
|
|
399
|
+
*/ addLayer(asset, settings) {
|
|
400
|
+
return this.insertLayer(new FoliageLayer(asset, settings));
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Inserts a layer, which is moved out of the foliage system it belongs to
|
|
404
|
+
* @param layer - The layer
|
|
405
|
+
* @param index - Position to insert at; appended when omitted
|
|
406
|
+
* @returns Index of the layer
|
|
407
|
+
*/ insertLayer(layer, index) {
|
|
408
|
+
if (layer._owner) {
|
|
409
|
+
layer._owner.detachLayer(layer);
|
|
410
|
+
}
|
|
411
|
+
const at = Math.max(0, Math.min(index ?? this._layers.length, this._layers.length));
|
|
412
|
+
this._layers.splice(at, 0, layer);
|
|
413
|
+
this.adoptLayer(layer);
|
|
414
|
+
this.invalidate();
|
|
415
|
+
return at;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Removes a layer and its instances
|
|
419
|
+
* @param index - Layer index
|
|
420
|
+
*/ removeLayer(index) {
|
|
421
|
+
const layer = this._layers[index];
|
|
422
|
+
if (layer) {
|
|
423
|
+
this.detachLayer(layer);
|
|
424
|
+
layer._dispose();
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Removes every layer
|
|
429
|
+
*/ clearLayers() {
|
|
430
|
+
for (const layer of this._layers){
|
|
431
|
+
layer._owner = null;
|
|
432
|
+
layer._dispose();
|
|
433
|
+
}
|
|
434
|
+
this._layers = [];
|
|
435
|
+
this.invalidate();
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Replaces the layer list. Layers already in the system keep their loaded assets, layers
|
|
439
|
+
* left out are disposed, and layers of another system are moved here.
|
|
440
|
+
* @param layers - The new layers, in order
|
|
441
|
+
*/ setLayers(layers) {
|
|
442
|
+
const next = layers.filter((layer, i)=>layers.indexOf(layer) === i);
|
|
443
|
+
for (const layer of this._layers){
|
|
444
|
+
if (!next.includes(layer)) {
|
|
445
|
+
layer._owner = null;
|
|
446
|
+
layer._dispose();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
for (const layer of next){
|
|
450
|
+
if (layer._owner && layer._owner !== this) {
|
|
451
|
+
layer._owner.detachLayer(layer);
|
|
452
|
+
}
|
|
453
|
+
this.adoptLayer(layer);
|
|
454
|
+
}
|
|
455
|
+
this._layers = next;
|
|
456
|
+
this.invalidate();
|
|
457
|
+
}
|
|
458
|
+
/** Makes a layer use this system's chunk and mask cell sizes */ adoptLayer(layer) {
|
|
459
|
+
layer._owner = this;
|
|
460
|
+
layer._generated.setChunkSize(this._chunkSize);
|
|
461
|
+
layer._manual.setChunkSize(this._chunkSize);
|
|
462
|
+
layer._mask.cellSize = this._maskCellSize;
|
|
463
|
+
}
|
|
464
|
+
/** Takes a layer out of the list, keeping its instances */ detachLayer(layer) {
|
|
465
|
+
const index = this._layers.indexOf(layer);
|
|
466
|
+
if (index >= 0) {
|
|
467
|
+
this._layers.splice(index, 1);
|
|
468
|
+
layer._owner = null;
|
|
469
|
+
this.invalidate();
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
/**
|
|
473
|
+
* Changes the settings of a layer
|
|
474
|
+
* @param index - Layer index
|
|
475
|
+
* @param settings - Settings to change; omitted ones are kept
|
|
476
|
+
*/ setLayerSettings(index, settings) {
|
|
477
|
+
const layer = this._layers[index];
|
|
478
|
+
if (!layer) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
if (settings.asset !== undefined) {
|
|
482
|
+
layer.asset = settings.asset;
|
|
483
|
+
}
|
|
484
|
+
layer._castShadow = settings.castShadow ?? layer._castShadow;
|
|
485
|
+
layer._cullDistance = settings.cullDistance ?? layer._cullDistance;
|
|
486
|
+
layer._alignToNormal = settings.alignToNormal ?? layer._alignToNormal;
|
|
487
|
+
if (settings.collision !== undefined) {
|
|
488
|
+
layer._collision = copyCollision(settings.collision);
|
|
489
|
+
}
|
|
490
|
+
if (settings.generation) {
|
|
491
|
+
layer._generation = mergeGeneration(layer._generation, settings.generation);
|
|
492
|
+
}
|
|
493
|
+
this.invalidate();
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Replaces the instances of a layer of one group
|
|
497
|
+
* @param index - Layer index
|
|
498
|
+
* @param data - {@link FOLIAGE_INSTANCE_STRIDE} floats per instance, see {@link FoliageLayer.getInstances}
|
|
499
|
+
* @param group - Which instances to replace. Default 'manual'; 'all' empties the generated
|
|
500
|
+
* group and puts the instances in the manual one.
|
|
501
|
+
*/ setInstances(index, data, group = 'manual') {
|
|
502
|
+
const layer = this._layers[index];
|
|
503
|
+
if (!layer) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
const touched = new Set();
|
|
507
|
+
if (group === 'generated') {
|
|
508
|
+
for (const key of layer._generated.clear()){
|
|
509
|
+
touched.add(key);
|
|
510
|
+
}
|
|
511
|
+
layer._generated.add(data, touched);
|
|
512
|
+
} else {
|
|
513
|
+
if (group === 'all') {
|
|
514
|
+
for (const key of layer._generated.clear()){
|
|
515
|
+
touched.add(key);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
for (const key of layer._manual.clear()){
|
|
519
|
+
touched.add(key);
|
|
520
|
+
}
|
|
521
|
+
layer._manual.add(data, touched);
|
|
522
|
+
}
|
|
523
|
+
this.invalidateChunks(layer, touched);
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Appends instances to a layer
|
|
527
|
+
* @param index - Layer index
|
|
528
|
+
* @param data - {@link FOLIAGE_INSTANCE_STRIDE} floats per instance, see {@link FoliageLayer.getInstances}
|
|
529
|
+
* @param group - Which group receives them. Default 'manual'.
|
|
530
|
+
*/ addInstances(index, data, group = 'manual') {
|
|
531
|
+
const layer = this._layers[index];
|
|
532
|
+
if (!layer) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
const touched = new Set();
|
|
536
|
+
(group === 'generated' ? layer._generated : layer._manual).add(data, touched);
|
|
537
|
+
this.invalidateChunks(layer, touched);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Replaces the generated instances of a layer standing within a region
|
|
541
|
+
*
|
|
542
|
+
* @remarks
|
|
543
|
+
* Used by the foliage generator: the region is aligned with the candidate grid, so the
|
|
544
|
+
* instances removed and the ones added are of the same candidates.
|
|
545
|
+
* @param index - Layer index
|
|
546
|
+
* @param region - `[minX, minZ, maxX, maxZ]`, local; instances with minX ≤ x < maxX and
|
|
547
|
+
* minZ ≤ z < maxZ are replaced
|
|
548
|
+
* @param data - The new instances of the region
|
|
549
|
+
*/ replaceGeneratedInRegion(index, region, data) {
|
|
550
|
+
const layer = this._layers[index];
|
|
551
|
+
if (!layer) {
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const touched = new Set();
|
|
555
|
+
layer._generated.removeWhere(region, (d, o)=>d[o] >= region[0] && d[o] < region[2] && d[o + 1] >= region[1] && d[o + 1] < region[3], touched);
|
|
556
|
+
layer._generated.add(data, touched);
|
|
557
|
+
this.invalidateChunks(layer, touched);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Changes the density mask of a layer within a circle; the generated instances are not
|
|
561
|
+
* regenerated, see {@link FoliageLayer.mask}
|
|
562
|
+
* @param index - Layer index
|
|
563
|
+
* @param x - Circle centre x, local
|
|
564
|
+
* @param z - Circle centre z, local
|
|
565
|
+
* @param radius - Circle radius
|
|
566
|
+
* @param amount - Change at full weight, -1 to 1; negative erases
|
|
567
|
+
* @param falloff - Fraction of the radius over which the brush weight falls to 0
|
|
568
|
+
* @returns The changed region, or null
|
|
569
|
+
*/ paintMask(index, x, z, radius, amount, falloff = 0.5) {
|
|
570
|
+
return this._layers[index]?._mask.paintCircle(x, z, radius, amount, falloff) ?? null;
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Moves the density mask of a layer within a circle towards a density; the generated
|
|
574
|
+
* instances are not regenerated, see {@link FoliageLayer.mask}
|
|
575
|
+
* @param index - Layer index
|
|
576
|
+
* @param x - Circle centre x, local
|
|
577
|
+
* @param z - Circle centre z, local
|
|
578
|
+
* @param radius - Circle radius
|
|
579
|
+
* @param density - Density inside the circle, 0 to 1
|
|
580
|
+
* @param falloff - Fraction of the radius over which it blends into what is around
|
|
581
|
+
* @returns The changed region, or null
|
|
582
|
+
*/ fillMaskCircle(index, x, z, radius, density, falloff = 0.5) {
|
|
583
|
+
return this._layers[index]?._mask.blendCircle(x, z, radius, density, falloff) ?? null;
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Erases manual instances within a circle, by brush weight: an instance goes where the weight
|
|
587
|
+
* of the brush there is above a value derived from its position, so a soft brush thins them
|
|
588
|
+
* out towards its edge, the same way every time
|
|
589
|
+
* @param index - Layer index
|
|
590
|
+
* @param x - Circle centre x, local
|
|
591
|
+
* @param z - Circle centre z, local
|
|
592
|
+
* @param radius - Circle radius
|
|
593
|
+
* @param strength - Weight at the centre, 0 to 1
|
|
594
|
+
* @param weight - Brush weight from the distance over the radius
|
|
595
|
+
* @returns Number of instances removed
|
|
596
|
+
*/ eraseManualInstances(index, x, z, radius, strength, weight) {
|
|
597
|
+
const layer = this._layers[index];
|
|
598
|
+
if (!layer || !(radius > 0) || !(strength > 0)) {
|
|
599
|
+
return 0;
|
|
600
|
+
}
|
|
601
|
+
const touched = new Set();
|
|
602
|
+
const removed = layer._manual.removeWhere([
|
|
603
|
+
x - radius,
|
|
604
|
+
z - radius,
|
|
605
|
+
x + radius,
|
|
606
|
+
z + radius
|
|
607
|
+
], (d, o)=>{
|
|
608
|
+
const t = Math.hypot(d[o] - x, d[o + 1] - z) / radius;
|
|
609
|
+
return t < 1 && strength * weight(t) > positionHash(d[o], d[o + 1]);
|
|
610
|
+
}, touched);
|
|
611
|
+
this.invalidateChunks(layer, touched);
|
|
612
|
+
return removed;
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Removes the instances standing within a circle
|
|
616
|
+
*
|
|
617
|
+
* @remarks
|
|
618
|
+
* Generated instances are removed by clearing the density mask under the circle, so
|
|
619
|
+
* regenerating the layer does not bring them back.
|
|
620
|
+
* @param x - Circle centre x, local to the foliage system
|
|
621
|
+
* @param z - Circle centre z, local to the foliage system
|
|
622
|
+
* @param radius - Circle radius
|
|
623
|
+
* @param index - Layer index, or -1 for every layer
|
|
624
|
+
* @param group - Which instances. Default 'all'.
|
|
625
|
+
* @returns Number of instances removed
|
|
626
|
+
*/ removeInstancesInCircle(x, z, radius, index = -1, group = 'all') {
|
|
627
|
+
let removed = 0;
|
|
628
|
+
const r2 = radius * radius;
|
|
629
|
+
const region = [
|
|
630
|
+
x - radius,
|
|
631
|
+
z - radius,
|
|
632
|
+
x + radius,
|
|
633
|
+
z + radius
|
|
634
|
+
];
|
|
635
|
+
const inside = (d, o)=>{
|
|
636
|
+
const dx = d[o] - x;
|
|
637
|
+
const dz = d[o + 1] - z;
|
|
638
|
+
return dx * dx + dz * dz <= r2;
|
|
639
|
+
};
|
|
640
|
+
this._layers.forEach((layer, i)=>{
|
|
641
|
+
if (index >= 0 && i !== index) {
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
const touched = new Set();
|
|
645
|
+
if (group !== 'generated') {
|
|
646
|
+
removed += layer._manual.removeWhere(region, inside, touched);
|
|
647
|
+
}
|
|
648
|
+
if (group !== 'manual') {
|
|
649
|
+
layer._mask.fillCircle(x, z, radius, 0);
|
|
650
|
+
removed += layer._generated.removeWhere(region, inside, touched);
|
|
651
|
+
}
|
|
652
|
+
this.invalidateChunks(layer, touched);
|
|
653
|
+
});
|
|
654
|
+
return removed;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* The terrain the instances stand on: the closest {@link ClipmapTerrain} ancestor
|
|
658
|
+
*/ get terrain() {
|
|
659
|
+
let node = this.parent;
|
|
660
|
+
while(node){
|
|
661
|
+
if (node.isClipmapTerrain()) {
|
|
662
|
+
return node;
|
|
663
|
+
}
|
|
664
|
+
node = node.parent;
|
|
665
|
+
}
|
|
666
|
+
return null;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Schedules every chunk to be rebuilt; called by the methods changing layers
|
|
670
|
+
*/ invalidate() {
|
|
671
|
+
this._dirty = true;
|
|
672
|
+
// Also called while the base constructor attaches the node, before the fields exist
|
|
673
|
+
this._dirtyChunks?.clear();
|
|
674
|
+
this.scene?.queuePerCameraUpdateNode(this);
|
|
675
|
+
}
|
|
676
|
+
/** Schedules some chunks of a layer to be rebuilt */ invalidateChunks(layer, keys) {
|
|
677
|
+
if (this._dirty) {
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
let set = this._dirtyChunks.get(layer);
|
|
681
|
+
for (const key of keys){
|
|
682
|
+
if (!set) {
|
|
683
|
+
set = new Set();
|
|
684
|
+
this._dirtyChunks.set(layer, set);
|
|
685
|
+
}
|
|
686
|
+
set.add(key);
|
|
687
|
+
}
|
|
688
|
+
if (set) {
|
|
689
|
+
this.scene?.queuePerCameraUpdateNode(this);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
/** {@inheritDoc SceneNode.updatePerCamera} */ updatePerCamera(camera, _elapsedInSeconds, _deltaInSeconds) {
|
|
693
|
+
this.scene?.queuePerCameraUpdateNode(this);
|
|
694
|
+
const terrain = this.terrain;
|
|
695
|
+
// Rebuilt once the terrain heights the instances stand on are known again after a change
|
|
696
|
+
if (terrain !== this._builtTerrain || terrain && terrain.heightData !== this._builtHeightData) {
|
|
697
|
+
if (!terrain || terrain.heightData) {
|
|
698
|
+
this._dirty = true;
|
|
699
|
+
} else {
|
|
700
|
+
terrain.whenHeightDataReady();
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
if (this._dirty) {
|
|
704
|
+
this._dirty = false;
|
|
705
|
+
this._dirtyChunks.clear();
|
|
706
|
+
this.rebuild();
|
|
707
|
+
} else if (this._dirtyChunks.size > 0) {
|
|
708
|
+
this.rebuildChunks();
|
|
709
|
+
}
|
|
710
|
+
const eye = camera.getWorldPosition();
|
|
711
|
+
const ox = this.worldMatrix.m03;
|
|
712
|
+
const oz = this.worldMatrix.m23;
|
|
713
|
+
const ex = eye.x - ox;
|
|
714
|
+
const ez = eye.z - oz;
|
|
715
|
+
for (const chunks of this._chunks.values()){
|
|
716
|
+
for (const chunk of chunks.values()){
|
|
717
|
+
const dx = Math.max(chunk.minX - ex, 0, ex - chunk.maxX);
|
|
718
|
+
const dz = Math.max(chunk.minZ - ez, 0, ez - chunk.maxZ);
|
|
719
|
+
const visible = dx * dx + dz * dz <= chunk.layer._cullDistance * chunk.layer._cullDistance;
|
|
720
|
+
if (visible !== chunk.visible) {
|
|
721
|
+
chunk.visible = visible;
|
|
722
|
+
chunk.group.showState = visible ? 'inherit' : 'hidden';
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
/** @internal */ _onAttached() {
|
|
728
|
+
super._onAttached();
|
|
729
|
+
this.invalidate();
|
|
730
|
+
}
|
|
731
|
+
/** Rebuilds every chunk, loading the layer assets first */ rebuild() {
|
|
732
|
+
const serial = ++this._buildSerial;
|
|
733
|
+
const terrain = this.terrain;
|
|
734
|
+
const heightData = terrain?.heightData ?? null;
|
|
735
|
+
const pending = this._layers.filter((layer)=>!layer._parts).map((layer)=>this.loadLayer(layer));
|
|
736
|
+
Promise.all(pending).then(()=>{
|
|
737
|
+
if (serial !== this._buildSerial || this.disposed) {
|
|
738
|
+
return;
|
|
739
|
+
}
|
|
740
|
+
this.clearChunks();
|
|
741
|
+
this._builtTerrain = terrain;
|
|
742
|
+
this._builtHeightData = heightData;
|
|
743
|
+
for (const layer of this._layers){
|
|
744
|
+
for (const key of new Set([
|
|
745
|
+
...layer._generated.keys(),
|
|
746
|
+
...layer._manual.keys()
|
|
747
|
+
])){
|
|
748
|
+
this.buildChunk(layer, key, terrain);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
/** Rebuilds the chunks marked dirty, once every layer is loaded */ rebuildChunks() {
|
|
754
|
+
if (this._layers.some((layer)=>!layer._parts)) {
|
|
755
|
+
// A layer still loading: its full rebuild picks the changes up
|
|
756
|
+
this._dirty = true;
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
const terrain = this._builtTerrain;
|
|
760
|
+
for (const [layer, keys] of this._dirtyChunks){
|
|
761
|
+
if (!this._layers.includes(layer)) {
|
|
762
|
+
continue;
|
|
763
|
+
}
|
|
764
|
+
for (const key of keys){
|
|
765
|
+
this.disposeChunk(layer, key);
|
|
766
|
+
this.buildChunk(layer, key, terrain);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
this._dirtyChunks.clear();
|
|
770
|
+
}
|
|
771
|
+
clearChunks() {
|
|
772
|
+
for (const chunks of this._chunks.values()){
|
|
773
|
+
for (const chunk of chunks.values()){
|
|
774
|
+
chunk.group.remove();
|
|
775
|
+
chunk.group.dispose();
|
|
776
|
+
chunk.colliders?.dispose();
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
this._chunks.clear();
|
|
780
|
+
}
|
|
781
|
+
disposeChunk(layer, key) {
|
|
782
|
+
const chunks = this._chunks.get(layer);
|
|
783
|
+
const chunk = chunks?.get(key);
|
|
784
|
+
if (chunk) {
|
|
785
|
+
chunk.group.remove();
|
|
786
|
+
chunk.group.dispose();
|
|
787
|
+
chunk.colliders?.dispose();
|
|
788
|
+
chunks.delete(key);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
loadLayer(layer) {
|
|
792
|
+
if (!layer._asset) {
|
|
793
|
+
// A layer just added in the editor, nothing to draw until an asset is picked
|
|
794
|
+
layer._parts = [];
|
|
795
|
+
return Promise.resolve();
|
|
796
|
+
}
|
|
797
|
+
if (!layer._loading) {
|
|
798
|
+
const asset = layer._asset;
|
|
799
|
+
layer._loading = (async ()=>{
|
|
800
|
+
const scene = this.scene;
|
|
801
|
+
const tmp = new SceneNode(scene);
|
|
802
|
+
tmp.remove();
|
|
803
|
+
let root = null;
|
|
804
|
+
try {
|
|
805
|
+
const manager = getEngine().resourceManager;
|
|
806
|
+
root = asset.toLowerCase().endsWith('.zprefab') ? await manager.instantiatePrefab(tmp, asset) : await manager.fetchModel(asset, scene) ?? null;
|
|
807
|
+
if (root) {
|
|
808
|
+
root.parent = tmp;
|
|
809
|
+
root.position.setXYZ(0, 0, 0);
|
|
810
|
+
root.rotation.identity();
|
|
811
|
+
root.scale.setXYZ(1, 1, 1);
|
|
812
|
+
}
|
|
813
|
+
} catch (err) {
|
|
814
|
+
console.error(`Foliage: cannot load ${asset}: ${err}`);
|
|
815
|
+
}
|
|
816
|
+
const parts = [];
|
|
817
|
+
const colliders = [];
|
|
818
|
+
let bounds = null;
|
|
819
|
+
root?.iterate((node)=>{
|
|
820
|
+
for (const c of node.physics?.colliders ?? []){
|
|
821
|
+
if (PRIMITIVE_COLLIDER_SHAPES.includes(c.shape)) {
|
|
822
|
+
colliders.push({
|
|
823
|
+
matrix: new Matrix4x4(node.worldMatrix),
|
|
824
|
+
shape: c.shape,
|
|
825
|
+
size: new Vector3(c.size),
|
|
826
|
+
radius: c.radius,
|
|
827
|
+
height: c.height,
|
|
828
|
+
offset: new Vector3(c.offset),
|
|
829
|
+
friction: c.friction,
|
|
830
|
+
restitution: c.restitution,
|
|
831
|
+
layer: c.layer
|
|
832
|
+
});
|
|
833
|
+
} else {
|
|
834
|
+
console.warn(`Foliage: ${c.shape} colliders of ${asset} are skipped`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (node.isMesh() && node.primitive && node.material) {
|
|
838
|
+
const box = node.primitive.getBoundingVolume()?.toAABB();
|
|
839
|
+
if (box) {
|
|
840
|
+
const world = AABB.transform(box, node.worldMatrix);
|
|
841
|
+
if (bounds) {
|
|
842
|
+
bounds.union(world);
|
|
843
|
+
} else {
|
|
844
|
+
bounds = world;
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
if (node.skeletonName || node.morphTargetGroups?.length) {
|
|
848
|
+
console.warn(`Foliage: skinned and morphed meshes of ${asset} are skipped`);
|
|
849
|
+
return false;
|
|
850
|
+
}
|
|
851
|
+
const material = node.material;
|
|
852
|
+
parts.push({
|
|
853
|
+
primitive: new DRef(node.primitive),
|
|
854
|
+
// Instance materials are what lets the copies of a mesh be drawn instanced
|
|
855
|
+
material: new DRef(material.$isInstance ? material : material.createInstance()),
|
|
856
|
+
matrix: new Matrix4x4(node.worldMatrix)
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
return false;
|
|
860
|
+
});
|
|
861
|
+
tmp.dispose();
|
|
862
|
+
if (layer._asset !== asset) {
|
|
863
|
+
// The asset changed while loading: the meshes are of the old one
|
|
864
|
+
for (const part of parts){
|
|
865
|
+
part.primitive.dispose();
|
|
866
|
+
part.material.dispose();
|
|
867
|
+
}
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
if (parts.length === 0) {
|
|
871
|
+
console.error(`Foliage: ${asset} has no mesh to scatter`);
|
|
872
|
+
}
|
|
873
|
+
layer._parts = parts;
|
|
874
|
+
layer._bounds = bounds;
|
|
875
|
+
layer._assetColliders = colliders;
|
|
876
|
+
layer._loading = null;
|
|
877
|
+
})();
|
|
878
|
+
}
|
|
879
|
+
return layer._loading;
|
|
880
|
+
}
|
|
881
|
+
/** Builds the batch group and colliders of one chunk of a layer, from both instance groups */ buildChunk(layer, key, terrain) {
|
|
882
|
+
const parts = layer._parts;
|
|
883
|
+
if (!parts || parts.length === 0) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const sources = [
|
|
887
|
+
layer._manual.get(key),
|
|
888
|
+
layer._generated.get(key)
|
|
889
|
+
].filter((d)=>!!d && d.length > 0);
|
|
890
|
+
if (sources.length === 0) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
const size = this._chunkSize;
|
|
894
|
+
const [cx, cz] = key.split(',').map(Number);
|
|
895
|
+
const ox = this.worldMatrix.m03;
|
|
896
|
+
const oy = this.worldMatrix.m13;
|
|
897
|
+
const oz = this.worldMatrix.m23;
|
|
898
|
+
const templates = layer._colliderTemplates();
|
|
899
|
+
// Painted instances on any surface store their height; the others stand on the terrain
|
|
900
|
+
const onTerrain = (generated)=>!generated || layer._generation.surface === 'terrain';
|
|
901
|
+
const group = new BatchGroup(this.scene);
|
|
902
|
+
group.sealed = true;
|
|
903
|
+
group.name = `Foliage chunk ${cx},${cz}`;
|
|
904
|
+
group.parent = this;
|
|
905
|
+
// One static body per chunk, with a collider node per instance and template
|
|
906
|
+
let colliders = null;
|
|
907
|
+
if (templates.length > 0) {
|
|
908
|
+
colliders = new SceneNode(this.scene);
|
|
909
|
+
colliders.sealed = true;
|
|
910
|
+
colliders.name = `Foliage colliders ${cx},${cz}`;
|
|
911
|
+
colliders.parent = this;
|
|
912
|
+
const body = new RigidBody();
|
|
913
|
+
body.motionType = 'static';
|
|
914
|
+
colliders.physics = new NodePhysics({
|
|
915
|
+
body
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
const manual = layer._manual.get(key);
|
|
919
|
+
for (const d of sources){
|
|
920
|
+
const followTerrain = onTerrain(d !== manual);
|
|
921
|
+
for(let o = 0; o < d.length; o += FOLIAGE_INSTANCE_STRIDE){
|
|
922
|
+
const x = d[o];
|
|
923
|
+
const z = d[o + 1];
|
|
924
|
+
let y = d[o + 2];
|
|
925
|
+
let upright = true;
|
|
926
|
+
if (terrain && followTerrain) {
|
|
927
|
+
const h = terrain.getHeightAt(x + ox, z + oz);
|
|
928
|
+
if (h !== null) {
|
|
929
|
+
y += h - oy;
|
|
930
|
+
if (layer._alignToNormal > 0 && terrain.getNormalAt(x + ox, z + oz, tmpNormal)) {
|
|
931
|
+
Quaternion.unitVectorToUnitVector(UP, tmpNormal, tmpAlign);
|
|
932
|
+
Quaternion.slerp(Quaternion.identity(tmpQuat), tmpAlign, layer._alignToNormal, tmpAlign);
|
|
933
|
+
upright = false;
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
Quaternion.fromAxisAngle(UP, d[o + 3], tmpQuat);
|
|
938
|
+
if (!upright) {
|
|
939
|
+
Quaternion.multiply(tmpAlign, tmpQuat, tmpQuat);
|
|
940
|
+
}
|
|
941
|
+
const s = d[o + 4];
|
|
942
|
+
Matrix4x4.compose(tmpScale.setXYZ(s, s, s), tmpQuat, tmpPos.setXYZ(x, y, z), tmpMatrix);
|
|
943
|
+
for (const part of parts){
|
|
944
|
+
const mesh = new Mesh(this.scene, part.primitive.get(), part.material.get());
|
|
945
|
+
mesh.sealed = true;
|
|
946
|
+
mesh.castShadow = layer._castShadow;
|
|
947
|
+
mesh.gpuPickable = false;
|
|
948
|
+
mesh.parent = group;
|
|
949
|
+
mesh.setLocalTransform(Matrix4x4.multiply(tmpMatrix, part.matrix, tmpMatrix2));
|
|
950
|
+
// Chunks are built in updatePerCamera, after the frame update that refreshes the
|
|
951
|
+
// matrices meshes draw with; without this the group draws them where they were created
|
|
952
|
+
mesh.syncDrawableTransform();
|
|
953
|
+
}
|
|
954
|
+
for (const t of templates){
|
|
955
|
+
const node = new SceneNode(this.scene);
|
|
956
|
+
node.sealed = true;
|
|
957
|
+
node.parent = colliders;
|
|
958
|
+
node.setLocalTransform(Matrix4x4.multiply(tmpMatrix, t.matrix, tmpMatrix2));
|
|
959
|
+
const collider = new Collider();
|
|
960
|
+
collider.shape = t.shape;
|
|
961
|
+
collider.size = t.size;
|
|
962
|
+
collider.radius = t.radius;
|
|
963
|
+
collider.height = t.height;
|
|
964
|
+
collider.offset = t.offset;
|
|
965
|
+
collider.friction = t.friction;
|
|
966
|
+
collider.restitution = t.restitution;
|
|
967
|
+
collider.layer = t.layer;
|
|
968
|
+
node.physics = new NodePhysics({
|
|
969
|
+
colliders: [
|
|
970
|
+
collider
|
|
971
|
+
]
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
let chunks = this._chunks.get(layer);
|
|
977
|
+
if (!chunks) {
|
|
978
|
+
chunks = new Map();
|
|
979
|
+
this._chunks.set(layer, chunks);
|
|
980
|
+
}
|
|
981
|
+
chunks.set(key, {
|
|
982
|
+
layer,
|
|
983
|
+
key,
|
|
984
|
+
group,
|
|
985
|
+
colliders,
|
|
986
|
+
minX: cx * size,
|
|
987
|
+
minZ: cz * size,
|
|
988
|
+
maxX: (cx + 1) * size,
|
|
989
|
+
maxZ: (cz + 1) * size,
|
|
990
|
+
visible: true
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* The chunk nodes built for this system, to tell them apart from other scene nodes
|
|
995
|
+
* (e.g. to skip the foliage's own colliders when looking for the ground)
|
|
996
|
+
* @param node - A scene node
|
|
997
|
+
* @returns Whether the node is, or is below, a chunk of this system
|
|
998
|
+
*/ ownsNode(node) {
|
|
999
|
+
while(node && node !== this){
|
|
1000
|
+
if (node.parent === this && node.sealed) {
|
|
1001
|
+
return true;
|
|
1002
|
+
}
|
|
1003
|
+
node = node.parent;
|
|
1004
|
+
}
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
1007
|
+
/** {@inheritDoc SceneNode.onDispose} */ onDispose() {
|
|
1008
|
+
this._buildSerial++;
|
|
1009
|
+
this.clearChunks();
|
|
1010
|
+
for (const layer of this._layers){
|
|
1011
|
+
layer._owner = null;
|
|
1012
|
+
layer._dispose();
|
|
1013
|
+
}
|
|
1014
|
+
this._layers = [];
|
|
1015
|
+
super.onDispose();
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
export { FOLIAGE_INSTANCE_STRIDE, FoliageDensityMask, FoliageLayer, FoliageSystem };
|
|
1020
|
+
//# sourceMappingURL=foliage.js.map
|