@woosh/meep-engine 3.11.3 → 3.12.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 +14 -18
- package/editor/SelectionVisualizer.js +127 -127
- package/editor/entity_world_bounds.js +27 -27
- package/editor/particles/rebuildParticleEmitter.js +31 -31
- package/editor/process/SymbolicDisplayProcess.js +1 -1
- package/editor/process/symbolic/CameraSymbolicDisplay.js +146 -146
- package/editor/process/symbolic/LightSymbolicDisplay.js +172 -172
- package/editor/prototypeEditorShell.js +156 -156
- package/editor/selection/editor_pick.js +130 -130
- package/editor/selection/screen_rect_frustum.js +119 -119
- package/editor/tools/v2/TransformControls.js +971 -971
- package/package.json +1 -1
- package/src/engine/Engine.d.ts +3 -3
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +726 -726
- package/src/engine/EngineHarness.d.ts +1 -1
- package/src/engine/EngineHarness.js +590 -590
- package/src/engine/ecs/HIERARCHY_REVIEW.md +717 -717
- package/src/engine/ecs/terrain/ecs/BuildLightTexture.d.ts +1 -1
- package/src/engine/ecs/terrain/ecs/BuildLightTexture.js +27 -27
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts +1 -5
- package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
- package/src/engine/ecs/terrain/ecs/Terrain.js +790 -790
- package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +129 -129
- package/src/engine/graphics/ecs/camera/camera_find_active.d.ts +1 -1
- package/src/engine/graphics/ecs/camera/camera_find_active.js +28 -28
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.d.ts +1 -1
- package/src/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +428 -428
- package/src/engine/graphics/ecs/water/Water.d.ts +1 -1
- package/src/engine/graphics/ecs/water/Water.js +104 -104
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +1 -1
- package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +1 -1
- package/src/engine/graphics3/AnimationGraphSystem.d.ts +65 -0
- package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -0
- package/src/engine/graphics3/AnimationGraphSystem.js +329 -0
- package/src/engine/graphics3/AnimationGraphSystem3.d.ts +6 -61
- package/src/engine/graphics3/AnimationGraphSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/AnimationGraphSystem3.js +6 -325
- package/src/engine/graphics3/AnimationSystem.d.ts +76 -0
- package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -0
- package/src/engine/graphics3/AnimationSystem.js +352 -0
- package/src/engine/graphics3/AnimationSystem3.d.ts +6 -72
- package/src/engine/graphics3/AnimationSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/AnimationSystem3.js +6 -348
- package/src/engine/graphics3/CameraSystem.d.ts +49 -0
- package/src/engine/graphics3/CameraSystem.d.ts.map +1 -0
- package/src/engine/graphics3/CameraSystem.js +170 -0
- package/src/engine/graphics3/CameraSystem3.d.ts +6 -45
- package/src/engine/graphics3/CameraSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/CameraSystem3.js +6 -166
- package/src/engine/graphics3/DebugDrawSystem.d.ts +72 -0
- package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -0
- package/src/engine/graphics3/DebugDrawSystem.js +205 -0
- package/src/engine/graphics3/DebugDrawSystem3.d.ts +6 -68
- package/src/engine/graphics3/DebugDrawSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/DebugDrawSystem3.js +6 -201
- package/src/engine/graphics3/DecalSystem.d.ts +81 -0
- package/src/engine/graphics3/DecalSystem.d.ts.map +1 -0
- package/src/engine/graphics3/DecalSystem.js +441 -0
- package/src/engine/graphics3/DecalSystem3.d.ts +6 -77
- package/src/engine/graphics3/DecalSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/DecalSystem3.js +6 -437
- package/src/engine/graphics3/FogOfWarSystem.d.ts +74 -0
- package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -0
- package/src/engine/graphics3/FogOfWarSystem.js +265 -0
- package/src/engine/graphics3/FogOfWarSystem3.d.ts +6 -70
- package/src/engine/graphics3/FogOfWarSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/FogOfWarSystem3.js +6 -261
- package/src/engine/graphics3/GraphicsEngine.d.ts +340 -0
- package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -0
- package/src/engine/graphics3/GraphicsEngine.js +790 -0
- package/src/engine/graphics3/GraphicsEngine3.d.ts +6 -336
- package/src/engine/graphics3/GraphicsEngine3.d.ts.map +1 -1
- package/src/engine/graphics3/GraphicsEngine3.js +6 -786
- package/src/engine/graphics3/HighlightSystem.d.ts +82 -0
- package/src/engine/graphics3/HighlightSystem.d.ts.map +1 -0
- package/src/engine/graphics3/HighlightSystem.js +241 -0
- package/src/engine/graphics3/HighlightSystem3.d.ts +6 -78
- package/src/engine/graphics3/HighlightSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/HighlightSystem3.js +6 -237
- package/src/engine/graphics3/LightSystem.d.ts +40 -0
- package/src/engine/graphics3/LightSystem.d.ts.map +1 -0
- package/src/engine/graphics3/LightSystem.js +211 -0
- package/src/engine/graphics3/LightSystem3.d.ts +6 -36
- package/src/engine/graphics3/LightSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/LightSystem3.js +6 -207
- package/src/engine/graphics3/MeshSystem.d.ts +106 -0
- package/src/engine/graphics3/MeshSystem.d.ts.map +1 -0
- package/src/engine/graphics3/MeshSystem.js +647 -0
- package/src/engine/graphics3/MeshSystem3.d.ts +6 -102
- package/src/engine/graphics3/MeshSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/MeshSystem3.js +6 -642
- package/src/engine/graphics3/ParticipatingMedia.d.ts +2 -2
- package/src/engine/graphics3/ParticipatingMedia.js +203 -203
- package/src/engine/graphics3/ParticipatingMediaSystem.d.ts +87 -0
- package/src/engine/graphics3/ParticipatingMediaSystem.d.ts.map +1 -0
- package/src/engine/graphics3/ParticipatingMediaSystem.js +184 -0
- package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts +6 -83
- package/src/engine/graphics3/ParticipatingMediaSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/ParticipatingMediaSystem3.js +6 -180
- package/src/engine/graphics3/ParticleEmitterSystem.d.ts +76 -0
- package/src/engine/graphics3/ParticleEmitterSystem.d.ts.map +1 -0
- package/src/engine/graphics3/ParticleEmitterSystem.js +289 -0
- package/src/engine/graphics3/ParticleEmitterSystem3.d.ts +6 -72
- package/src/engine/graphics3/ParticleEmitterSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/ParticleEmitterSystem3.js +6 -285
- package/src/engine/graphics3/PathDisplaySystem.d.ts +70 -0
- package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -0
- package/src/engine/graphics3/PathDisplaySystem.js +309 -0
- package/src/engine/graphics3/PathDisplaySystem3.d.ts +6 -66
- package/src/engine/graphics3/PathDisplaySystem3.d.ts.map +1 -1
- package/src/engine/graphics3/PathDisplaySystem3.js +6 -305
- package/src/engine/graphics3/Pickable.d.ts +1 -1
- package/src/engine/graphics3/Pickable.js +65 -65
- package/src/engine/graphics3/PickingQuery.d.ts +1 -1
- package/src/engine/graphics3/PickingQuery.js +48 -48
- package/src/engine/graphics3/PickingSystem.d.ts +67 -0
- package/src/engine/graphics3/PickingSystem.d.ts.map +1 -0
- package/src/engine/graphics3/PickingSystem.js +273 -0
- package/src/engine/graphics3/PickingSystem3.d.ts +6 -63
- package/src/engine/graphics3/PickingSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/PickingSystem3.js +6 -269
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts +47 -0
- package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -0
- package/src/engine/graphics3/ShadedGeometrySystem.js +169 -0
- package/src/engine/graphics3/ShadedGeometrySystem3.d.ts +6 -43
- package/src/engine/graphics3/ShadedGeometrySystem3.d.ts.map +1 -1
- package/src/engine/graphics3/ShadedGeometrySystem3.js +6 -165
- package/src/engine/graphics3/TerrainSystem.d.ts +80 -0
- package/src/engine/graphics3/TerrainSystem.d.ts.map +1 -0
- package/src/engine/graphics3/TerrainSystem.js +566 -0
- package/src/engine/graphics3/TerrainSystem3.d.ts +6 -76
- package/src/engine/graphics3/TerrainSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/TerrainSystem3.js +6 -562
- package/src/engine/graphics3/TooltipComponentSystem.d.ts +65 -0
- package/src/engine/graphics3/TooltipComponentSystem.d.ts.map +1 -0
- package/src/engine/graphics3/TooltipComponentSystem.js +328 -0
- package/src/engine/graphics3/TooltipComponentSystem3.d.ts +6 -61
- package/src/engine/graphics3/TooltipComponentSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/TooltipComponentSystem3.js +6 -324
- package/src/engine/graphics3/Trail3DSystem.d.ts +73 -0
- package/src/engine/graphics3/Trail3DSystem.d.ts.map +1 -0
- package/src/engine/graphics3/Trail3DSystem.js +221 -0
- package/src/engine/graphics3/Trail3DSystem3.d.ts +6 -69
- package/src/engine/graphics3/Trail3DSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/Trail3DSystem3.js +6 -217
- package/src/engine/graphics3/VolumetricLightMap.d.ts +2 -2
- package/src/engine/graphics3/VolumetricLightMap.js +2 -2
- package/src/engine/graphics3/VolumetricLightMapSystem.d.ts +98 -0
- package/src/engine/graphics3/VolumetricLightMapSystem.d.ts.map +1 -0
- package/src/engine/graphics3/VolumetricLightMapSystem.js +261 -0
- package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts +6 -94
- package/src/engine/graphics3/VolumetricLightMapSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/VolumetricLightMapSystem3.js +10 -261
- package/src/engine/graphics3/WaterSystem.d.ts +59 -0
- package/src/engine/graphics3/WaterSystem.d.ts.map +1 -0
- package/src/engine/graphics3/WaterSystem.js +293 -0
- package/src/engine/graphics3/WaterSystem3.d.ts +6 -55
- package/src/engine/graphics3/WaterSystem3.d.ts.map +1 -1
- package/src/engine/graphics3/WaterSystem3.js +6 -289
- package/src/engine/graphics3/animation/AnimationGraphController.d.ts +1 -1
- package/src/engine/graphics3/animation/AnimationGraphController.js +518 -518
- package/src/engine/graphics3/animation/ClipListPlayer.d.ts +1 -1
- package/src/engine/graphics3/animation/ClipListPlayer.js +263 -263
- package/src/engine/graphics3/animation/ClipPlayback.d.ts +2 -2
- package/src/engine/graphics3/animation/ClipPlayback.js +84 -84
- package/src/engine/graphics3/decal/graph_build_decal_clusters.d.ts.map +1 -1
- package/src/engine/graphics3/decal/graph_build_decal_clusters.js +6 -8
- package/src/engine/graphics3/decal/shader_cluster_assign_decals.d.ts +0 -7
- package/src/engine/graphics3/decal/shader_cluster_assign_decals.d.ts.map +1 -1
- package/src/engine/graphics3/decal/shader_cluster_assign_decals.js +1 -1
- package/src/engine/graphics3/decal/shader_cull_decals_frustum.d.ts +0 -4
- package/src/engine/graphics3/decal/shader_cull_decals_frustum.d.ts.map +1 -1
- package/src/engine/graphics3/decal/shader_cull_decals_frustum.js +1 -1
- package/src/engine/graphics3/highlight/pack_highlight_table.d.ts +2 -2
- package/src/engine/graphics3/highlight/pack_highlight_table.d.ts.map +1 -1
- package/src/engine/graphics3/highlight/pack_highlight_table.js +74 -74
- package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts +1 -1
- package/src/engine/graphics3/pose/query_entity_node_world_pose.js +101 -101
- package/src/engine/graphics3/preview/make_model_thumbnail.d.ts +3 -3
- package/src/engine/graphics3/preview/make_model_thumbnail.d.ts.map +1 -1
- package/src/engine/graphics3/preview/make_model_thumbnail.js +48 -48
- package/src/engine/graphics3/shade_camera_projection_ray.d.ts +1 -1
- package/src/engine/graphics3/shade_camera_projection_ray.js +77 -77
- package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +1 -1
- package/src/engine/graphics3/shade_node_to_entity_composition.js +84 -84
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.js +0 -1
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.js +0 -1
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts.map +1 -1
- package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.js +133 -134
- package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.js +202 -202
- package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.js +249 -249
- package/src/shade/renderer/STATIC_GRAPHICS_ENGINE_ASSETS.js +1 -1
- package/src/shade/renderer/animation/pose/PosePlayback.d.ts +2 -1
- package/src/shade/renderer/animation/pose/PosePlayback.d.ts.map +1 -1
- package/src/shade/renderer/animation/pose/PosePlayback.js +2 -1
- package/src/shade/renderer/animation/shader_animation_apply.d.ts.map +1 -1
- package/src/shade/renderer/animation/shader_animation_apply.js +128 -129
- package/src/shade/renderer/animation/shader_animation_pose_resolve.d.ts.map +1 -1
- package/src/shade/renderer/animation/shader_animation_pose_resolve.js +176 -177
- package/src/shade/renderer/animation/shader_animation_tick.d.ts.map +1 -1
- package/src/shade/renderer/animation/shader_animation_tick.js +125 -126
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_geometry_sphere_derive.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_geometry_sphere_derive.js +98 -99
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +212 -213
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_scatter.js +120 -121
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_skinned_mesh_bounds_refresh.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_skinned_mesh_bounds_refresh.js +75 -76
- package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/shader_meshlet_apply_skinning.js +371 -372
- package/src/shade/renderer/animation/skinning/shader_skin_matrix_prep.d.ts.map +1 -1
- package/src/shade/renderer/animation/skinning/shader_skin_matrix_prep.js +104 -105
- package/src/shade/renderer/atmosphere/shader_multiscatter_lut.d.ts.map +1 -1
- package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +0 -1
- package/src/shade/renderer/buffer/graph_inspect_gpu_buffer.js +136 -136
- package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUDatabase.js +2 -3
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +319 -319
- package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.d.ts.map +1 -1
- package/src/shade/renderer/buffer/util/shader_buffer_copy_storage_to_storage.js +41 -42
- package/src/shade/renderer/buffer/util/shader_buffer_intra_copy_regions.d.ts.map +1 -1
- package/src/shade/renderer/buffer/util/shader_buffer_intra_copy_regions.js +83 -84
- package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +177 -170
- package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +918 -918
- package/src/shade/renderer/geometry/bvh/record_blas_refit.js +4 -4
- package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.d.ts.map +1 -1
- package/src/shade/renderer/geometry/bvh/shader_blas_refit_leaf_bounds.js +121 -122
- package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +774 -774
- package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts +1 -1
- package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/shader_meshlet_metadata_address_patch.js +69 -70
- package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.js +367 -367
- package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.js +184 -184
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1115 -1115
- package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +217 -217
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +142 -143
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_morton.js +0 -1
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_refit.js +140 -141
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_histogram.js +0 -1
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.d.ts.map +1 -1
- package/src/shade/renderer/gpu_primitive/sort/shader_radix_sort_scatter.js +0 -1
- package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
- package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +191 -198
- package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.d.ts +0 -6
- package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.d.ts.map +1 -1
- package/src/shade/renderer/light/cluster/cull/shader_cull_lights_frustum.js +120 -120
- package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.d.ts +0 -4
- package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.d.ts.map +1 -1
- package/src/shade/renderer/light/cluster/cull/shader_cull_lights_hzb.js +112 -112
- package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
- package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +4 -7
- package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.d.ts +0 -6
- package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.d.ts.map +1 -1
- package/src/shade/renderer/light/cluster/shader_cluster_assign_lights.js +1 -1
- package/src/shade/renderer/lightmap/bake/raster/shader_lightmap_to_viz_buffer.js +222 -222
- package/src/shade/renderer/lightmap/bake/tile/shader_lightmap_tile_composit.js +81 -81
- package/src/shade/renderer/material/GPUMaterialContext.js +380 -380
- package/src/shade/renderer/material/standard/fragment_gbuffer_vt.js +169 -169
- package/src/shade/renderer/object_property/shader_object_property_process.d.ts.map +1 -1
- package/src/shade/renderer/object_property/shader_object_property_process.js +83 -84
- package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.d.ts.map +1 -1
- package/src/shade/renderer/object_property/shader_refresh_all_mesh_bounds.js +57 -58
- package/src/shade/renderer/particles/prototypeParticleSystem.js +2 -3
- package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emit.js +117 -118
- package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +77 -79
- package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_render.js +190 -183
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +99 -100
- package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts.map +1 -1
- package/src/shade/renderer/particles/sort/shader_particle_sort.js +158 -161
- package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +184 -184
- package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.js +119 -119
- package/src/shade/renderer/postprocess/dof/raymarch/graph_postprocess_dof.js +3 -4
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_raymarch_compute.js +266 -267
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/dof/raymarch/shader_dof_slice_compute.js +270 -271
- package/src/shade/renderer/postprocess/nss/shader_nss_concat.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_concat.js +68 -69
- package/src/shade/renderer/postprocess/nss/shader_nss_conv_layer.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_conv_layer.js +225 -226
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +49 -50
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +291 -292
- package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +230 -231
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts +16 -2
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.js +12 -12
- package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +2 -2
- package/src/shade/renderer/rasterize/bucket/shader_count_meshlets_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_count_meshlets_by_material.js +68 -69
- package/src/shade/renderer/rasterize/bucket/shader_make_meshlet_indirect_draw_commands_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_make_meshlet_indirect_draw_commands_by_material.js +59 -60
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +0 -1
- package/src/shade/renderer/rasterize/bucket/shader_sort_meshlets_by_material.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/shader_sort_meshlets_by_material.js +64 -65
- package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.js +168 -169
- package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.js +160 -161
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +109 -110
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +115 -116
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_classify_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_classify_bucket.js +139 -140
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_count_by_rasterization_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_count_by_rasterization_bucket.js +91 -92
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +88 -89
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +90 -91
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +65 -66
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +89 -90
- package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.js +169 -170
- package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +89 -90
- package/src/shade/renderer/rasterize/expand/mesh/shader_meshes_to_meshlet_counts.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/mesh/shader_meshes_to_meshlet_counts.js +61 -62
- package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +153 -154
- package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +144 -144
- package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +88 -88
- package/src/shade/renderer/rasterize/native/oit/shader_oit_generate_moments.js +217 -217
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments.js +179 -179
- package/src/shade/renderer/rasterize/native/oit/shader_oit_resolve_moments_ibl.js +178 -178
- package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +194 -194
- package/src/shade/renderer/rasterize/native/viz/viz_rasterization_opaque_pass_descriptor.js +76 -76
- package/src/shade/renderer/restir/di/shader_restir_di_denoise.d.ts.map +1 -1
- package/src/shade/renderer/restir/di/shader_restir_di_denoise.js +136 -137
- package/src/shade/renderer/restir/di/shader_restir_di_resample.d.ts.map +1 -1
- package/src/shade/renderer/restir/di/shader_restir_di_resample.js +129 -130
- package/src/shade/renderer/restir/di/shader_restir_di_resolve.d.ts.map +1 -1
- package/src/shade/renderer/restir/di/shader_restir_di_resolve.js +99 -100
- package/src/shade/renderer/restir/di/shader_restir_di_spatial.d.ts.map +1 -1
- package/src/shade/renderer/restir/di/shader_restir_di_spatial.js +116 -117
- package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +383 -383
- package/src/shade/renderer/scene/hierarchy/graph_scene_update_transform_hierarchy_naive.js +123 -123
- package/src/shade/renderer/scene/hierarchy/shader_nodes_record_input_positions.d.ts.map +1 -1
- package/src/shade/renderer/scene/hierarchy/shader_nodes_record_input_positions.js +64 -65
- package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy.d.ts.map +1 -1
- package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy.js +372 -373
- package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy_naive.d.ts.map +1 -1
- package/src/shade/renderer/scene/hierarchy/shader_nodes_update_transform_hierarchy_naive.js +111 -112
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.d.ts.map +1 -1
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +0 -1
- package/src/shade/renderer/shader/ComputeShader.d.ts +24 -3
- package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
- package/src/shade/renderer/shader/ComputeShader.js +350 -296
- package/src/shade/renderer/shader/ImageShader.js +217 -217
- package/src/shade/renderer/shader/ShaderDescriptor.d.ts +5 -0
- package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
- package/src/shade/renderer/shader/ShaderDescriptor.js +20 -0
- package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.d.ts +47 -0
- package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.d.ts.map +1 -0
- package/src/shade/renderer/shader/util/compute_infer_workgroup_size_from_source.js +448 -0
- package/src/shade/renderer/shadow/map/csm/graph_csm_setup_cascades.js +3 -3
- package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.js +3 -3
- package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.js +2 -2
- package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.js +4 -4
- package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.d.ts.map +1 -1
- package/src/shade/renderer/texture/virtual/build_shader_vt_feedback.js +231 -232
- package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +1 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +12 -15
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts +3 -2
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/shader_volumetrics_build_lighting.js +3 -2
- package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts +0 -2
- package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.d.ts.map +1 -1
- package/src/shade/renderer/volumetrics/taa/shader_volumetrics_taa.js +17 -6
- package/src/shade/renderer/water/shader_water.js +246 -246
- package/src/shade/vitest.setup.mjs +1 -1
- package/src/shade/wgsl/emulator/ComputeShaderEmulator.js +291 -291
- package/src/view/minimap/Minimap.d.ts +2 -2
- package/src/view/minimap/Minimap.d.ts.map +1 -1
- package/src/view/minimap/Minimap.js +225 -225
- package/src/view/minimap/dom/MinimapCameraView.d.ts +3 -3
- package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
- package/src/view/minimap/dom/MinimapCameraView.js +206 -206
- package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.d.ts +0 -3
- package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.d.ts.map +0 -1
- package/src/shade/renderer/shadow/map/csm/shader/shader_setup_cascades.js +0 -20
|
@@ -1,372 +1,371 @@
|
|
|
1
|
-
import { mat4x4f, u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
2
|
-
import { WebGPUArray } from "../../shader/type/WebGPUArray.js";
|
|
3
|
-
import {
|
|
4
|
-
MESHLET_METADATA_FLAGS
|
|
5
|
-
} from "../../geometry/meshlet/encoding/MESHLET_METADATA_FLAGS.js";
|
|
6
|
-
import {
|
|
7
|
-
MESHLET_METADATA_STRUCT
|
|
8
|
-
} from "../../geometry/meshlet/encoding/MESHLET_METADATA_STRUCT.js";
|
|
9
|
-
import { chunk_scene_database_access } from "../../scene/chunk_scene_database_access.js";
|
|
10
|
-
import { chunk_mat4_inverse } from "../../shader/chunk/geometry/mat4/chunk_mat4_inverse.js";
|
|
11
|
-
import {
|
|
12
|
-
chunk_meshlet_compute_attribute_section_offset
|
|
13
|
-
} from "../../shader/chunk/geometry/meshlet/chunk_meshlet_compute_attribute_section_offset.js";
|
|
14
|
-
import {
|
|
15
|
-
chunk_decode_vertex_normal
|
|
16
|
-
} from "../../shader/chunk/geometry/normal/chunk_decode_vertex_normal.js";
|
|
17
|
-
import {
|
|
18
|
-
chunk_encode_vertex_normal
|
|
19
|
-
} from "../../shader/chunk/geometry/normal/chunk_encode_vertex_normal.js";
|
|
20
|
-
import {
|
|
21
|
-
chunk_decode_vertex_tangent
|
|
22
|
-
} from "../../shader/chunk/geometry/normal/tangent/chunk_decode_vertex_tangent.js";
|
|
23
|
-
import {
|
|
24
|
-
chunk_encode_vertex_tangent
|
|
25
|
-
} from "../../shader/chunk/geometry/normal/tangent/chunk_encode_vertex_tangent.js";
|
|
26
|
-
import {
|
|
27
|
-
chunk_dual_quat_transform_direction
|
|
28
|
-
} from "../../shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_direction.js";
|
|
29
|
-
import {
|
|
30
|
-
chunk_dual_quat_transform_point
|
|
31
|
-
} from "../../shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js";
|
|
32
|
-
import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
|
|
33
|
-
import { ComputeShader } from "../../shader/ComputeShader.js";
|
|
34
|
-
import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
35
|
-
import { chunk_skin_blend_mesh_local } from "./chunk_skin_blend_mesh_local.js";
|
|
36
|
-
import { SKINNING_BINDING_STRUCT } from "./SKINNING_BINDING_STRUCT.js";
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Workgroup size — also the per-meshlet vertex stride. Each thread
|
|
40
|
-
* handles vertex `local_id`, then loops by `WORKGROUP_SIZE` for
|
|
41
|
-
* meshlets larger than the workgroup.
|
|
42
|
-
*/
|
|
43
|
-
export const SKINNING_WORKGROUP_SIZE = 64;
|
|
44
|
-
|
|
45
|
-
const resources = new ShaderResourceSetDescriptor();
|
|
46
|
-
resources.createGroup()
|
|
47
|
-
// First workgroup of the current sub-dispatch. WebGPU caps
|
|
48
|
-
// `dispatchWorkgroups` along each dimension at the device's
|
|
49
|
-
// `maxComputeWorkgroupsPerDimension` (65535 minimum per the
|
|
50
|
-
// spec, often higher in practice), so the host paginates large
|
|
51
|
-
// totals into multiple dispatches and increments this offset
|
|
52
|
-
// between them. The shader adds it to `wg_id.x` before indexing
|
|
53
|
-
// `dispatch_lookup`, so the per-pair mapping stays a flat array
|
|
54
|
-
// regardless of how many sub-dispatches it gets split into.
|
|
55
|
-
.addUniform('dispatch_offset', u32)
|
|
56
|
-
.addStorageBuffer('meshlet_data', WebGPUArray.from(u32), true)
|
|
57
|
-
.addStorageBuffer('meshlet_metadata', WebGPUArray.from(MESHLET_METADATA_STRUCT), false)
|
|
58
|
-
.addStorageBuffer('skin_matrices', WebGPUArray.from(mat4x4f), false)
|
|
59
|
-
.addStorageBuffer('prev_skin_matrices', WebGPUArray.from(mat4x4f), false)
|
|
60
|
-
.addStorageBuffer('skinning_bindings', WebGPUArray.from(SKINNING_BINDING_STRUCT), false)
|
|
61
|
-
.addStorageBuffer('dispatch_lookup', WebGPUArray.from(u32), false)
|
|
62
|
-
// Per-meshlet base u32-word offset into the side-buffer that
|
|
63
|
-
// holds previous-frame mesh-local positions. Sentinel
|
|
64
|
-
// `0xFFFFFFFF` for non-clone meshlets — those skip the
|
|
65
|
-
// prev-position write entirely, since the velocity post-pass
|
|
66
|
-
// will fall back to per-object math for them.
|
|
67
|
-
.addStorageBuffer('prev_position_offsets', WebGPUArray.from(u32), false)
|
|
68
|
-
.addStorageBuffer('prev_positions', WebGPUArray.from(u32), true)
|
|
69
|
-
// Scene-graph state — read-only. We need the SkinnedMesh's
|
|
70
|
-
// `transforms` row to source the live `mesh.transform_global`
|
|
71
|
-
// for the post-DQ position cancellation (see commentary in main).
|
|
72
|
-
.addStorageBuffer('scene_database', WebGPUArray.from(u32), false);
|
|
73
|
-
|
|
74
|
-
// One workgroup per (binding, meshlet) pair. The host builds
|
|
75
|
-
// `dispatch_lookup` as a flat (binding_index, meshlet_local) pair
|
|
76
|
-
// table and dispatches one workgroup per pair, so workgroup_id.x
|
|
77
|
-
// directly indexes the table.
|
|
78
|
-
//
|
|
79
|
-
// Skinning math is dual-quaternion: each of the 4 per-vertex joints
|
|
80
|
-
// contributes a DQ (built from its skin matrix), the four DQs blend
|
|
81
|
-
// by weight (with antipodal correction), and the result transforms
|
|
82
|
-
// position via the full screw motion and normal/tangent via just the
|
|
83
|
-
// rotation part. DQ blending avoids the candy-wrapper artifacts of
|
|
84
|
-
// linear matrix blending and lets normals + tangents transform with
|
|
85
|
-
// the same blended frame, no matrix inversion or gimbal-style
|
|
86
|
-
// interpolation issues.
|
|
87
|
-
//
|
|
88
|
-
// Per-attribute compression handling: Position is always
|
|
89
|
-
// uncompressed (one entry per vertex, vertex_count*3 words). For
|
|
90
|
-
// Normal and Tangent, the meshlet flags say whether they were
|
|
91
|
-
// collapsed to a single shared entry across all vertices. Per-vertex
|
|
92
|
-
// skinning into a shared slot would race, so the shader skips the
|
|
93
|
-
// write for any attribute where the flag is set — the cloned slot
|
|
94
|
-
// keeps the source's value and lighting on those vertices uses an
|
|
95
|
-
// undeformed direction. For typical character meshes
|
|
96
|
-
// normals/tangents vary per vertex and so don't get the all-equal
|
|
97
|
-
// collapse, so this skip rarely engages in practice.
|
|
98
|
-
//
|
|
99
|
-
// SkinningJoints + SkinningWeights are read-only here. They CAN be
|
|
100
|
-
// compressed — a meshlet where every vertex shares the same joint
|
|
101
|
-
// palette (e.g. simple test meshes that bind every vertex to the
|
|
102
|
-
// same two bones) collapses Joints to one shared u32-pair, and the
|
|
103
|
-
// shader then has to broadcast that single entry across all vertex
|
|
104
|
-
// reads instead of striding by 2 words per vertex. Same logic for
|
|
105
|
-
// Weights (less common in practice — weights usually vary per
|
|
106
|
-
// vertex even on simple meshes — but the flag handling is free).
|
|
107
|
-
const body = CodeChunk.from(
|
|
108
|
-
//language=WGSL
|
|
109
|
-
`
|
|
110
|
-
@compute @workgroup_size(${SKINNING_WORKGROUP_SIZE},1,1)
|
|
111
|
-
fn main(
|
|
112
|
-
@builtin(workgroup_id) wg_id: vec3<u32>,
|
|
113
|
-
@builtin(local_invocation_index) local_id: u32,
|
|
114
|
-
) {
|
|
115
|
-
// dispatch_offset lets the host paginate large dispatches —
|
|
116
|
-
// WebGPU's per-dimension cap is 65535 workgroups, but a busy
|
|
117
|
-
// scene's (binding, meshlet) pair total can exceed that.
|
|
118
|
-
let pair_index = wg_id.x + dispatch_offset;
|
|
119
|
-
let lookup_base = pair_index * 2u;
|
|
120
|
-
let binding_index = dispatch_lookup[lookup_base];
|
|
121
|
-
let meshlet_local = dispatch_lookup[lookup_base + 1u];
|
|
122
|
-
|
|
123
|
-
let binding = skinning_bindings[binding_index];
|
|
124
|
-
|
|
125
|
-
// The glTF skinning formula's leftmost factor is
|
|
126
|
-
// inverse(meshNode.worldMatrix), which lands the per-vertex
|
|
127
|
-
// output in mesh-local space ready for the rasterizer's
|
|
128
|
-
// mesh.world re-application (see
|
|
129
|
-
// chunk_mesh_resolve_vertex_position_world.js). It goes into
|
|
130
|
-
// each *matrix*, not onto the blended position — see
|
|
131
|
-
// chunk_skin_blend_mesh_local for why the difference is the
|
|
132
|
-
// whole ball game. Both positions and directions therefore come
|
|
133
|
-
// out of the blend already in mesh-local space, and the
|
|
134
|
-
// rasterizer's own transforms take them to world.
|
|
135
|
-
//
|
|
136
|
-
// mesh_instance_index is the row of the SkinnedMesh in the
|
|
137
|
-
// meshes table; mesh.node is its transforms-table row.
|
|
138
|
-
// Workgroup-uniform (one binding per workgroup), so the load +
|
|
139
|
-
// inverse amortize across every vertex this workgroup
|
|
140
|
-
// processes.
|
|
141
|
-
let mesh = scene_read_mesh(&scene_database, binding.mesh_instance_index);
|
|
142
|
-
let mesh_node = scene_read_node(&scene_database, mesh.node);
|
|
143
|
-
let inv_mesh_world = mat4_inverse(mesh_node.global);
|
|
144
|
-
|
|
145
|
-
let source_meshlet = meshlet_metadata[binding.source_meshlet_metadata_offset + meshlet_local];
|
|
146
|
-
let clone_meshlet = meshlet_metadata[binding.clone_meshlet_metadata_offset + meshlet_local];
|
|
147
|
-
|
|
148
|
-
let vertex_count = source_meshlet.vertex_count;
|
|
149
|
-
|
|
150
|
-
// Both source and clone share an attribute layout (the clone is a
|
|
151
|
-
// memcpy of the source's bytes with addresses patched), so attribute
|
|
152
|
-
// offsets relative to each meshlet's attribute section are identical.
|
|
153
|
-
let source_attr_section = meshlet_compute_attribute_section_offset(source_meshlet);
|
|
154
|
-
let clone_attr_section = meshlet_compute_attribute_section_offset(clone_meshlet);
|
|
155
|
-
|
|
156
|
-
// Walk the per-attribute layout, capturing the offset of each
|
|
157
|
-
// attribute we'll need. Order matches ENCODED_ORDERED_ATTRIBUTES:
|
|
158
|
-
// Position, Normal, Tangent, Color, UV0, UV1, SkinningJoints,
|
|
159
|
-
// SkinningWeights. Conditional skips for the compressible ones —
|
|
160
|
-
// when collapsed, the slot is a single shared entry rather than
|
|
161
|
-
// vertex_count entries.
|
|
162
|
-
let position_offset = 0u;
|
|
163
|
-
var attr_offset: u32 = vertex_count * 3u;
|
|
164
|
-
|
|
165
|
-
let normal_offset = attr_offset;
|
|
166
|
-
let normal_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_NORMAL}u) != 0u;
|
|
167
|
-
attr_offset = attr_offset + select(vertex_count, 1u, normal_compressed);
|
|
168
|
-
|
|
169
|
-
let tangent_offset = attr_offset;
|
|
170
|
-
let tangent_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_TANGENT}u) != 0u;
|
|
171
|
-
attr_offset = attr_offset + select(vertex_count, 1u, tangent_compressed);
|
|
172
|
-
|
|
173
|
-
let color_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_COLOR}u) != 0u;
|
|
174
|
-
attr_offset = attr_offset + select(vertex_count, 1u, color_compressed);
|
|
175
|
-
|
|
176
|
-
let uv0_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_TEXTURE_COORDINATES_0}u) != 0u;
|
|
177
|
-
attr_offset = attr_offset + select(vertex_count * 2u, 2u, uv0_compressed);
|
|
178
|
-
|
|
179
|
-
let uv1_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_TEXTURE_COORDINATES_1}u) != 0u;
|
|
180
|
-
attr_offset = attr_offset + select(vertex_count, 1u, uv1_compressed);
|
|
181
|
-
|
|
182
|
-
let joints_offset = attr_offset;
|
|
183
|
-
let joints_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_SKINNING_JOINTS}u) != 0u;
|
|
184
|
-
attr_offset = attr_offset + select(vertex_count * 2u, 2u, joints_compressed);
|
|
185
|
-
|
|
186
|
-
let weights_offset = attr_offset;
|
|
187
|
-
let weights_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_SKINNING_WEIGHTS}u) != 0u;
|
|
188
|
-
|
|
189
|
-
let skin_base = binding.skin_matrix_offset;
|
|
190
|
-
|
|
191
|
-
// Resolve the clone meshlet's per-meshlet base offset in the
|
|
192
|
-
// prev_positions side-buffer (or sentinel if this meshlet
|
|
193
|
-
// somehow isn't a clone — shouldn't happen in practice since
|
|
194
|
-
// the skinning shader only runs on (clone, skinned) pairs, but
|
|
195
|
-
// the check costs one load and one branch). Lifted out of the
|
|
196
|
-
// vertex loop because it's per-meshlet, not per-vertex.
|
|
197
|
-
let clone_meshlet_abs = binding.clone_meshlet_metadata_offset + meshlet_local;
|
|
198
|
-
let prev_pos_meshlet_base = prev_position_offsets[clone_meshlet_abs];
|
|
199
|
-
let has_prev_positions = prev_pos_meshlet_base != 0xFFFFFFFFu;
|
|
200
|
-
|
|
201
|
-
// Stride loop — one thread per WORKGROUP_SIZE-stride'd vertex slot.
|
|
202
|
-
// Most meshlets fit in a single iteration (vertex_count <=
|
|
203
|
-
// WORKGROUP_SIZE); larger ones cycle the workgroup over them.
|
|
204
|
-
var v: u32 = local_id;
|
|
205
|
-
loop {
|
|
206
|
-
if v >= vertex_count { break; }
|
|
207
|
-
|
|
208
|
-
// Joint indices: vec4<u16> packed into 2 u32s. When the
|
|
209
|
-
// attribute is compressed (every vertex of this meshlet
|
|
210
|
-
// shares the same palette), there's one shared pair at
|
|
211
|
-
// joints_offset rather than per-vertex strides; broadcast
|
|
212
|
-
// that single entry to every thread.
|
|
213
|
-
let joints_local_offset = select(v * 2u, 0u, joints_compressed);
|
|
214
|
-
let joints_addr = source_attr_section + joints_offset + joints_local_offset;
|
|
215
|
-
let raw_j0 = meshlet_data[joints_addr];
|
|
216
|
-
let raw_j1 = meshlet_data[joints_addr + 1u];
|
|
217
|
-
let j0 = raw_j0 & 0xFFFFu;
|
|
218
|
-
let j1 = (raw_j0 >> 16u) & 0xFFFFu;
|
|
219
|
-
let j2 = raw_j1 & 0xFFFFu;
|
|
220
|
-
let j3 = (raw_j1 >> 16u) & 0xFFFFu;
|
|
221
|
-
|
|
222
|
-
// Joint weights: vec4<unorm8> packed into 1 u32. Half the
|
|
223
|
-
// bandwidth of the previous f16×4 layout, sufficient
|
|
224
|
-
// precision for typical character skinning (step size
|
|
225
|
-
// ~0.4%, well under any visible artefact threshold).
|
|
226
|
-
// unpack4x8unorm returns vec4<f32> in [0, 1] with
|
|
227
|
-
// components matching byte 0..3 of the packed u32.
|
|
228
|
-
let weights_local_offset = select(v, 0u, weights_compressed);
|
|
229
|
-
let weights_addr = source_attr_section + weights_offset + weights_local_offset;
|
|
230
|
-
let weights = unpack4x8unorm(meshlet_data[weights_addr]);
|
|
231
|
-
|
|
232
|
-
// Build per-joint dual quaternions from the joint matrices, then
|
|
233
|
-
// blend with antipodal correction. The result represents the
|
|
234
|
-
// weighted-average screw motion the four matrices encode.
|
|
235
|
-
let dq_blend = skin_blend_mesh_local(
|
|
236
|
-
inv_mesh_world,
|
|
237
|
-
skin_matrices[skin_base + j0],
|
|
238
|
-
skin_matrices[skin_base + j1],
|
|
239
|
-
skin_matrices[skin_base + j2],
|
|
240
|
-
skin_matrices[skin_base + j3],
|
|
241
|
-
weights,
|
|
242
|
-
);
|
|
243
|
-
|
|
244
|
-
// Position: full screw motion (rotation + translation).
|
|
245
|
-
let pos_addr = source_attr_section + position_offset + v * 3u;
|
|
246
|
-
let p = vec3f(
|
|
247
|
-
bitcast<f32>(meshlet_data[pos_addr]),
|
|
248
|
-
bitcast<f32>(meshlet_data[pos_addr + 1u]),
|
|
249
|
-
bitcast<f32>(meshlet_data[pos_addr + 2u]),
|
|
250
|
-
);
|
|
251
|
-
// Position: full screw motion, already mesh-local — the
|
|
252
|
-
// rasterizer reapplies mesh.world to every mesh's vertices,
|
|
253
|
-
// skinned or not.
|
|
254
|
-
let p_skinned = dual_quat_transform_point(dq_blend, p);
|
|
255
|
-
|
|
256
|
-
let clone_pos_addr = clone_attr_section + position_offset + v * 3u;
|
|
257
|
-
meshlet_data[clone_pos_addr] = bitcast<u32>(p_skinned.x);
|
|
258
|
-
meshlet_data[clone_pos_addr + 1u] = bitcast<u32>(p_skinned.y);
|
|
259
|
-
meshlet_data[clone_pos_addr + 2u] = bitcast<u32>(p_skinned.z);
|
|
260
|
-
|
|
261
|
-
// Previous-frame mesh-local position: same dual-quat math,
|
|
262
|
-
// but driven by prev_skin_matrices instead of skin_matrices.
|
|
263
|
-
// Written to the side-buffer at the per-meshlet base
|
|
264
|
-
// offset + v * 3 u32 words. The velocity post-pass reads
|
|
265
|
-
// these to interpolate per-vertex skin deformation across
|
|
266
|
-
// the rasterized triangle (vs. just the rigid mesh.global
|
|
267
|
-
// → mesh.prev_global delta that Stage 1's path captures).
|
|
268
|
-
//
|
|
269
|
-
// Skipping the write when there's no slot for this meshlet
|
|
270
|
-
// (non-clone — shouldn't be hit by this shader's dispatch
|
|
271
|
-
// set, but the guard is cheap). The dual-quat build is
|
|
272
|
-
// also skipped to save the per-joint work.
|
|
273
|
-
if has_prev_positions {
|
|
274
|
-
// Mesh-local for the same reason the current-frame
|
|
275
|
-
// position is — the velocity pass reads these through
|
|
276
|
-
// the same rasterizer transform, so the space they live
|
|
277
|
-
// in has to match.
|
|
278
|
-
let prev_dq_blend = skin_blend_mesh_local(
|
|
279
|
-
inv_mesh_world,
|
|
280
|
-
prev_skin_matrices[skin_base + j0],
|
|
281
|
-
prev_skin_matrices[skin_base + j1],
|
|
282
|
-
prev_skin_matrices[skin_base + j2],
|
|
283
|
-
prev_skin_matrices[skin_base + j3],
|
|
284
|
-
weights,
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
let p_prev_skinned = dual_quat_transform_point(prev_dq_blend, p);
|
|
288
|
-
|
|
289
|
-
let prev_addr = prev_pos_meshlet_base + v * 3u;
|
|
290
|
-
prev_positions[prev_addr] = bitcast<u32>(p_prev_skinned.x);
|
|
291
|
-
prev_positions[prev_addr + 1u] = bitcast<u32>(p_prev_skinned.y);
|
|
292
|
-
prev_positions[prev_addr + 2u] = bitcast<u32>(p_prev_skinned.z);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Normal: rotation only. Skip when the source compressed it
|
|
296
|
-
// down to a single shared slot — per-vertex writes to that
|
|
297
|
-
// slot would race, and the rotated direction would be
|
|
298
|
-
// mostly-meaningless anyway since every vertex would compute
|
|
299
|
-
// a different one.
|
|
300
|
-
//
|
|
301
|
-
// Mesh-local like the position, and left there: the
|
|
302
|
-
// rasterizer applies the inverse-transpose of mesh.world's
|
|
303
|
-
// 3x3 to it, which is the glTF normal transform and takes a
|
|
304
|
-
// mesh-local direction to world (see fragment_gbuffer.js).
|
|
305
|
-
if !normal_compressed {
|
|
306
|
-
let n_raw_addr = source_attr_section + normal_offset + v;
|
|
307
|
-
let n = decode_vertex_normal(meshlet_data[n_raw_addr]);
|
|
308
|
-
let n_skinned = normalize(dual_quat_transform_direction(dq_blend, n));
|
|
309
|
-
|
|
310
|
-
let clone_n_addr = clone_attr_section + normal_offset + v;
|
|
311
|
-
meshlet_data[clone_n_addr] = encode_vertex_normal(n_skinned);
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Tangent: rotation only on the xyz; w (handedness) is a
|
|
315
|
-
// topological property, copied through unchanged.
|
|
316
|
-
if !tangent_compressed {
|
|
317
|
-
let t_raw_addr = source_attr_section + tangent_offset + v;
|
|
318
|
-
let t = decode_vertex_tangent(meshlet_data[t_raw_addr]);
|
|
319
|
-
let t_skinned_dir = normalize(dual_quat_transform_direction(dq_blend, t.xyz));
|
|
320
|
-
|
|
321
|
-
let clone_t_addr = clone_attr_section + tangent_offset + v;
|
|
322
|
-
meshlet_data[clone_t_addr] = encode_vertex_tangent(vec4f(t_skinned_dir, t.w));
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
v = v + ${SKINNING_WORKGROUP_SIZE}u;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
`, [
|
|
329
|
-
MESHLET_METADATA_STRUCT.declaration_chunk,
|
|
330
|
-
SKINNING_BINDING_STRUCT.declaration_chunk,
|
|
331
|
-
chunk_meshlet_compute_attribute_section_offset,
|
|
332
|
-
chunk_skin_blend_mesh_local,
|
|
333
|
-
chunk_dual_quat_transform_point,
|
|
334
|
-
chunk_dual_quat_transform_direction,
|
|
335
|
-
chunk_decode_vertex_normal,
|
|
336
|
-
chunk_encode_vertex_normal,
|
|
337
|
-
chunk_decode_vertex_tangent,
|
|
338
|
-
chunk_encode_vertex_tangent,
|
|
339
|
-
chunk_scene_database_access,
|
|
340
|
-
chunk_mat4_inverse,
|
|
341
|
-
]
|
|
342
|
-
);
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Compute shader that deforms cloned meshlet vertex regions in place
|
|
346
|
-
* using the per-frame skin matrices the prep pass produced. One
|
|
347
|
-
* workgroup per (binding, meshlet) pair, dispatched against a flat
|
|
348
|
-
* `dispatch_lookup` table the host builds when bindings change.
|
|
349
|
-
*
|
|
350
|
-
* Skins position (full transform) plus normal and tangent (rotation
|
|
351
|
-
* only) per vertex via dual-quaternion blending — the blended DQ
|
|
352
|
-
* captures the four per-vertex joints' screw motions weighted by
|
|
353
|
-
* the per-vertex `SkinningWeights`, and applies cleanly to both
|
|
354
|
-
* positions and unit-direction attributes without the matrix-blend
|
|
355
|
-
* candy-wrapper artifacts that would otherwise show up in the
|
|
356
|
-
* normal-transformed lighting.
|
|
357
|
-
*
|
|
358
|
-
* Source bytes stay untouched — other instances pointed at the
|
|
359
|
-
* source render normally. Per-meshlet bounds aren't refreshed by
|
|
360
|
-
* this pass; meshlets that animate outside the source bounds may
|
|
361
|
-
* cull incorrectly until a future bounds-refresh phase lands.
|
|
362
|
-
*
|
|
363
|
-
* Dispatch via `shader.dispatch({ group_count_x: total_meshlet_count, ... })`
|
|
364
|
-
* where `total_meshlet_count` is the sum of `meshlet_count` across
|
|
365
|
-
* every registered binding. Skip when `total_meshlet_count === 0`.
|
|
366
|
-
*/
|
|
367
|
-
export const shader_meshlet_apply_skinning = ComputeShader.from({
|
|
368
|
-
label: "Skinning/MeshletApplySkinning",
|
|
369
|
-
resources,
|
|
370
|
-
body,
|
|
371
|
-
|
|
372
|
-
});
|
|
1
|
+
import { mat4x4f, u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
2
|
+
import { WebGPUArray } from "../../shader/type/WebGPUArray.js";
|
|
3
|
+
import {
|
|
4
|
+
MESHLET_METADATA_FLAGS
|
|
5
|
+
} from "../../geometry/meshlet/encoding/MESHLET_METADATA_FLAGS.js";
|
|
6
|
+
import {
|
|
7
|
+
MESHLET_METADATA_STRUCT
|
|
8
|
+
} from "../../geometry/meshlet/encoding/MESHLET_METADATA_STRUCT.js";
|
|
9
|
+
import { chunk_scene_database_access } from "../../scene/chunk_scene_database_access.js";
|
|
10
|
+
import { chunk_mat4_inverse } from "../../shader/chunk/geometry/mat4/chunk_mat4_inverse.js";
|
|
11
|
+
import {
|
|
12
|
+
chunk_meshlet_compute_attribute_section_offset
|
|
13
|
+
} from "../../shader/chunk/geometry/meshlet/chunk_meshlet_compute_attribute_section_offset.js";
|
|
14
|
+
import {
|
|
15
|
+
chunk_decode_vertex_normal
|
|
16
|
+
} from "../../shader/chunk/geometry/normal/chunk_decode_vertex_normal.js";
|
|
17
|
+
import {
|
|
18
|
+
chunk_encode_vertex_normal
|
|
19
|
+
} from "../../shader/chunk/geometry/normal/chunk_encode_vertex_normal.js";
|
|
20
|
+
import {
|
|
21
|
+
chunk_decode_vertex_tangent
|
|
22
|
+
} from "../../shader/chunk/geometry/normal/tangent/chunk_decode_vertex_tangent.js";
|
|
23
|
+
import {
|
|
24
|
+
chunk_encode_vertex_tangent
|
|
25
|
+
} from "../../shader/chunk/geometry/normal/tangent/chunk_encode_vertex_tangent.js";
|
|
26
|
+
import {
|
|
27
|
+
chunk_dual_quat_transform_direction
|
|
28
|
+
} from "../../shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_direction.js";
|
|
29
|
+
import {
|
|
30
|
+
chunk_dual_quat_transform_point
|
|
31
|
+
} from "../../shader/chunk/geometry/quaternion/dual/chunk_dual_quat_transform_point.js";
|
|
32
|
+
import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
|
|
33
|
+
import { ComputeShader } from "../../shader/ComputeShader.js";
|
|
34
|
+
import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
35
|
+
import { chunk_skin_blend_mesh_local } from "./chunk_skin_blend_mesh_local.js";
|
|
36
|
+
import { SKINNING_BINDING_STRUCT } from "./SKINNING_BINDING_STRUCT.js";
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Workgroup size — also the per-meshlet vertex stride. Each thread
|
|
40
|
+
* handles vertex `local_id`, then loops by `WORKGROUP_SIZE` for
|
|
41
|
+
* meshlets larger than the workgroup.
|
|
42
|
+
*/
|
|
43
|
+
export const SKINNING_WORKGROUP_SIZE = 64;
|
|
44
|
+
|
|
45
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
46
|
+
resources.createGroup()
|
|
47
|
+
// First workgroup of the current sub-dispatch. WebGPU caps
|
|
48
|
+
// `dispatchWorkgroups` along each dimension at the device's
|
|
49
|
+
// `maxComputeWorkgroupsPerDimension` (65535 minimum per the
|
|
50
|
+
// spec, often higher in practice), so the host paginates large
|
|
51
|
+
// totals into multiple dispatches and increments this offset
|
|
52
|
+
// between them. The shader adds it to `wg_id.x` before indexing
|
|
53
|
+
// `dispatch_lookup`, so the per-pair mapping stays a flat array
|
|
54
|
+
// regardless of how many sub-dispatches it gets split into.
|
|
55
|
+
.addUniform('dispatch_offset', u32)
|
|
56
|
+
.addStorageBuffer('meshlet_data', WebGPUArray.from(u32), true)
|
|
57
|
+
.addStorageBuffer('meshlet_metadata', WebGPUArray.from(MESHLET_METADATA_STRUCT), false)
|
|
58
|
+
.addStorageBuffer('skin_matrices', WebGPUArray.from(mat4x4f), false)
|
|
59
|
+
.addStorageBuffer('prev_skin_matrices', WebGPUArray.from(mat4x4f), false)
|
|
60
|
+
.addStorageBuffer('skinning_bindings', WebGPUArray.from(SKINNING_BINDING_STRUCT), false)
|
|
61
|
+
.addStorageBuffer('dispatch_lookup', WebGPUArray.from(u32), false)
|
|
62
|
+
// Per-meshlet base u32-word offset into the side-buffer that
|
|
63
|
+
// holds previous-frame mesh-local positions. Sentinel
|
|
64
|
+
// `0xFFFFFFFF` for non-clone meshlets — those skip the
|
|
65
|
+
// prev-position write entirely, since the velocity post-pass
|
|
66
|
+
// will fall back to per-object math for them.
|
|
67
|
+
.addStorageBuffer('prev_position_offsets', WebGPUArray.from(u32), false)
|
|
68
|
+
.addStorageBuffer('prev_positions', WebGPUArray.from(u32), true)
|
|
69
|
+
// Scene-graph state — read-only. We need the SkinnedMesh's
|
|
70
|
+
// `transforms` row to source the live `mesh.transform_global`
|
|
71
|
+
// for the post-DQ position cancellation (see commentary in main).
|
|
72
|
+
.addStorageBuffer('scene_database', WebGPUArray.from(u32), false);
|
|
73
|
+
|
|
74
|
+
// One workgroup per (binding, meshlet) pair. The host builds
|
|
75
|
+
// `dispatch_lookup` as a flat (binding_index, meshlet_local) pair
|
|
76
|
+
// table and dispatches one workgroup per pair, so workgroup_id.x
|
|
77
|
+
// directly indexes the table.
|
|
78
|
+
//
|
|
79
|
+
// Skinning math is dual-quaternion: each of the 4 per-vertex joints
|
|
80
|
+
// contributes a DQ (built from its skin matrix), the four DQs blend
|
|
81
|
+
// by weight (with antipodal correction), and the result transforms
|
|
82
|
+
// position via the full screw motion and normal/tangent via just the
|
|
83
|
+
// rotation part. DQ blending avoids the candy-wrapper artifacts of
|
|
84
|
+
// linear matrix blending and lets normals + tangents transform with
|
|
85
|
+
// the same blended frame, no matrix inversion or gimbal-style
|
|
86
|
+
// interpolation issues.
|
|
87
|
+
//
|
|
88
|
+
// Per-attribute compression handling: Position is always
|
|
89
|
+
// uncompressed (one entry per vertex, vertex_count*3 words). For
|
|
90
|
+
// Normal and Tangent, the meshlet flags say whether they were
|
|
91
|
+
// collapsed to a single shared entry across all vertices. Per-vertex
|
|
92
|
+
// skinning into a shared slot would race, so the shader skips the
|
|
93
|
+
// write for any attribute where the flag is set — the cloned slot
|
|
94
|
+
// keeps the source's value and lighting on those vertices uses an
|
|
95
|
+
// undeformed direction. For typical character meshes
|
|
96
|
+
// normals/tangents vary per vertex and so don't get the all-equal
|
|
97
|
+
// collapse, so this skip rarely engages in practice.
|
|
98
|
+
//
|
|
99
|
+
// SkinningJoints + SkinningWeights are read-only here. They CAN be
|
|
100
|
+
// compressed — a meshlet where every vertex shares the same joint
|
|
101
|
+
// palette (e.g. simple test meshes that bind every vertex to the
|
|
102
|
+
// same two bones) collapses Joints to one shared u32-pair, and the
|
|
103
|
+
// shader then has to broadcast that single entry across all vertex
|
|
104
|
+
// reads instead of striding by 2 words per vertex. Same logic for
|
|
105
|
+
// Weights (less common in practice — weights usually vary per
|
|
106
|
+
// vertex even on simple meshes — but the flag handling is free).
|
|
107
|
+
const body = CodeChunk.from(
|
|
108
|
+
//language=WGSL
|
|
109
|
+
`
|
|
110
|
+
@compute @workgroup_size(${SKINNING_WORKGROUP_SIZE},1,1)
|
|
111
|
+
fn main(
|
|
112
|
+
@builtin(workgroup_id) wg_id: vec3<u32>,
|
|
113
|
+
@builtin(local_invocation_index) local_id: u32,
|
|
114
|
+
) {
|
|
115
|
+
// dispatch_offset lets the host paginate large dispatches —
|
|
116
|
+
// WebGPU's per-dimension cap is 65535 workgroups, but a busy
|
|
117
|
+
// scene's (binding, meshlet) pair total can exceed that.
|
|
118
|
+
let pair_index = wg_id.x + dispatch_offset;
|
|
119
|
+
let lookup_base = pair_index * 2u;
|
|
120
|
+
let binding_index = dispatch_lookup[lookup_base];
|
|
121
|
+
let meshlet_local = dispatch_lookup[lookup_base + 1u];
|
|
122
|
+
|
|
123
|
+
let binding = skinning_bindings[binding_index];
|
|
124
|
+
|
|
125
|
+
// The glTF skinning formula's leftmost factor is
|
|
126
|
+
// inverse(meshNode.worldMatrix), which lands the per-vertex
|
|
127
|
+
// output in mesh-local space ready for the rasterizer's
|
|
128
|
+
// mesh.world re-application (see
|
|
129
|
+
// chunk_mesh_resolve_vertex_position_world.js). It goes into
|
|
130
|
+
// each *matrix*, not onto the blended position — see
|
|
131
|
+
// chunk_skin_blend_mesh_local for why the difference is the
|
|
132
|
+
// whole ball game. Both positions and directions therefore come
|
|
133
|
+
// out of the blend already in mesh-local space, and the
|
|
134
|
+
// rasterizer's own transforms take them to world.
|
|
135
|
+
//
|
|
136
|
+
// mesh_instance_index is the row of the SkinnedMesh in the
|
|
137
|
+
// meshes table; mesh.node is its transforms-table row.
|
|
138
|
+
// Workgroup-uniform (one binding per workgroup), so the load +
|
|
139
|
+
// inverse amortize across every vertex this workgroup
|
|
140
|
+
// processes.
|
|
141
|
+
let mesh = scene_read_mesh(&scene_database, binding.mesh_instance_index);
|
|
142
|
+
let mesh_node = scene_read_node(&scene_database, mesh.node);
|
|
143
|
+
let inv_mesh_world = mat4_inverse(mesh_node.global);
|
|
144
|
+
|
|
145
|
+
let source_meshlet = meshlet_metadata[binding.source_meshlet_metadata_offset + meshlet_local];
|
|
146
|
+
let clone_meshlet = meshlet_metadata[binding.clone_meshlet_metadata_offset + meshlet_local];
|
|
147
|
+
|
|
148
|
+
let vertex_count = source_meshlet.vertex_count;
|
|
149
|
+
|
|
150
|
+
// Both source and clone share an attribute layout (the clone is a
|
|
151
|
+
// memcpy of the source's bytes with addresses patched), so attribute
|
|
152
|
+
// offsets relative to each meshlet's attribute section are identical.
|
|
153
|
+
let source_attr_section = meshlet_compute_attribute_section_offset(source_meshlet);
|
|
154
|
+
let clone_attr_section = meshlet_compute_attribute_section_offset(clone_meshlet);
|
|
155
|
+
|
|
156
|
+
// Walk the per-attribute layout, capturing the offset of each
|
|
157
|
+
// attribute we'll need. Order matches ENCODED_ORDERED_ATTRIBUTES:
|
|
158
|
+
// Position, Normal, Tangent, Color, UV0, UV1, SkinningJoints,
|
|
159
|
+
// SkinningWeights. Conditional skips for the compressible ones —
|
|
160
|
+
// when collapsed, the slot is a single shared entry rather than
|
|
161
|
+
// vertex_count entries.
|
|
162
|
+
let position_offset = 0u;
|
|
163
|
+
var attr_offset: u32 = vertex_count * 3u;
|
|
164
|
+
|
|
165
|
+
let normal_offset = attr_offset;
|
|
166
|
+
let normal_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_NORMAL}u) != 0u;
|
|
167
|
+
attr_offset = attr_offset + select(vertex_count, 1u, normal_compressed);
|
|
168
|
+
|
|
169
|
+
let tangent_offset = attr_offset;
|
|
170
|
+
let tangent_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_TANGENT}u) != 0u;
|
|
171
|
+
attr_offset = attr_offset + select(vertex_count, 1u, tangent_compressed);
|
|
172
|
+
|
|
173
|
+
let color_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_COLOR}u) != 0u;
|
|
174
|
+
attr_offset = attr_offset + select(vertex_count, 1u, color_compressed);
|
|
175
|
+
|
|
176
|
+
let uv0_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_TEXTURE_COORDINATES_0}u) != 0u;
|
|
177
|
+
attr_offset = attr_offset + select(vertex_count * 2u, 2u, uv0_compressed);
|
|
178
|
+
|
|
179
|
+
let uv1_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_TEXTURE_COORDINATES_1}u) != 0u;
|
|
180
|
+
attr_offset = attr_offset + select(vertex_count, 1u, uv1_compressed);
|
|
181
|
+
|
|
182
|
+
let joints_offset = attr_offset;
|
|
183
|
+
let joints_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_SKINNING_JOINTS}u) != 0u;
|
|
184
|
+
attr_offset = attr_offset + select(vertex_count * 2u, 2u, joints_compressed);
|
|
185
|
+
|
|
186
|
+
let weights_offset = attr_offset;
|
|
187
|
+
let weights_compressed = (source_meshlet.flags & ${MESHLET_METADATA_FLAGS.ATTRIBUTE_COMPRESSED_SKINNING_WEIGHTS}u) != 0u;
|
|
188
|
+
|
|
189
|
+
let skin_base = binding.skin_matrix_offset;
|
|
190
|
+
|
|
191
|
+
// Resolve the clone meshlet's per-meshlet base offset in the
|
|
192
|
+
// prev_positions side-buffer (or sentinel if this meshlet
|
|
193
|
+
// somehow isn't a clone — shouldn't happen in practice since
|
|
194
|
+
// the skinning shader only runs on (clone, skinned) pairs, but
|
|
195
|
+
// the check costs one load and one branch). Lifted out of the
|
|
196
|
+
// vertex loop because it's per-meshlet, not per-vertex.
|
|
197
|
+
let clone_meshlet_abs = binding.clone_meshlet_metadata_offset + meshlet_local;
|
|
198
|
+
let prev_pos_meshlet_base = prev_position_offsets[clone_meshlet_abs];
|
|
199
|
+
let has_prev_positions = prev_pos_meshlet_base != 0xFFFFFFFFu;
|
|
200
|
+
|
|
201
|
+
// Stride loop — one thread per WORKGROUP_SIZE-stride'd vertex slot.
|
|
202
|
+
// Most meshlets fit in a single iteration (vertex_count <=
|
|
203
|
+
// WORKGROUP_SIZE); larger ones cycle the workgroup over them.
|
|
204
|
+
var v: u32 = local_id;
|
|
205
|
+
loop {
|
|
206
|
+
if v >= vertex_count { break; }
|
|
207
|
+
|
|
208
|
+
// Joint indices: vec4<u16> packed into 2 u32s. When the
|
|
209
|
+
// attribute is compressed (every vertex of this meshlet
|
|
210
|
+
// shares the same palette), there's one shared pair at
|
|
211
|
+
// joints_offset rather than per-vertex strides; broadcast
|
|
212
|
+
// that single entry to every thread.
|
|
213
|
+
let joints_local_offset = select(v * 2u, 0u, joints_compressed);
|
|
214
|
+
let joints_addr = source_attr_section + joints_offset + joints_local_offset;
|
|
215
|
+
let raw_j0 = meshlet_data[joints_addr];
|
|
216
|
+
let raw_j1 = meshlet_data[joints_addr + 1u];
|
|
217
|
+
let j0 = raw_j0 & 0xFFFFu;
|
|
218
|
+
let j1 = (raw_j0 >> 16u) & 0xFFFFu;
|
|
219
|
+
let j2 = raw_j1 & 0xFFFFu;
|
|
220
|
+
let j3 = (raw_j1 >> 16u) & 0xFFFFu;
|
|
221
|
+
|
|
222
|
+
// Joint weights: vec4<unorm8> packed into 1 u32. Half the
|
|
223
|
+
// bandwidth of the previous f16×4 layout, sufficient
|
|
224
|
+
// precision for typical character skinning (step size
|
|
225
|
+
// ~0.4%, well under any visible artefact threshold).
|
|
226
|
+
// unpack4x8unorm returns vec4<f32> in [0, 1] with
|
|
227
|
+
// components matching byte 0..3 of the packed u32.
|
|
228
|
+
let weights_local_offset = select(v, 0u, weights_compressed);
|
|
229
|
+
let weights_addr = source_attr_section + weights_offset + weights_local_offset;
|
|
230
|
+
let weights = unpack4x8unorm(meshlet_data[weights_addr]);
|
|
231
|
+
|
|
232
|
+
// Build per-joint dual quaternions from the joint matrices, then
|
|
233
|
+
// blend with antipodal correction. The result represents the
|
|
234
|
+
// weighted-average screw motion the four matrices encode.
|
|
235
|
+
let dq_blend = skin_blend_mesh_local(
|
|
236
|
+
inv_mesh_world,
|
|
237
|
+
skin_matrices[skin_base + j0],
|
|
238
|
+
skin_matrices[skin_base + j1],
|
|
239
|
+
skin_matrices[skin_base + j2],
|
|
240
|
+
skin_matrices[skin_base + j3],
|
|
241
|
+
weights,
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Position: full screw motion (rotation + translation).
|
|
245
|
+
let pos_addr = source_attr_section + position_offset + v * 3u;
|
|
246
|
+
let p = vec3f(
|
|
247
|
+
bitcast<f32>(meshlet_data[pos_addr]),
|
|
248
|
+
bitcast<f32>(meshlet_data[pos_addr + 1u]),
|
|
249
|
+
bitcast<f32>(meshlet_data[pos_addr + 2u]),
|
|
250
|
+
);
|
|
251
|
+
// Position: full screw motion, already mesh-local — the
|
|
252
|
+
// rasterizer reapplies mesh.world to every mesh's vertices,
|
|
253
|
+
// skinned or not.
|
|
254
|
+
let p_skinned = dual_quat_transform_point(dq_blend, p);
|
|
255
|
+
|
|
256
|
+
let clone_pos_addr = clone_attr_section + position_offset + v * 3u;
|
|
257
|
+
meshlet_data[clone_pos_addr] = bitcast<u32>(p_skinned.x);
|
|
258
|
+
meshlet_data[clone_pos_addr + 1u] = bitcast<u32>(p_skinned.y);
|
|
259
|
+
meshlet_data[clone_pos_addr + 2u] = bitcast<u32>(p_skinned.z);
|
|
260
|
+
|
|
261
|
+
// Previous-frame mesh-local position: same dual-quat math,
|
|
262
|
+
// but driven by prev_skin_matrices instead of skin_matrices.
|
|
263
|
+
// Written to the side-buffer at the per-meshlet base
|
|
264
|
+
// offset + v * 3 u32 words. The velocity post-pass reads
|
|
265
|
+
// these to interpolate per-vertex skin deformation across
|
|
266
|
+
// the rasterized triangle (vs. just the rigid mesh.global
|
|
267
|
+
// → mesh.prev_global delta that Stage 1's path captures).
|
|
268
|
+
//
|
|
269
|
+
// Skipping the write when there's no slot for this meshlet
|
|
270
|
+
// (non-clone — shouldn't be hit by this shader's dispatch
|
|
271
|
+
// set, but the guard is cheap). The dual-quat build is
|
|
272
|
+
// also skipped to save the per-joint work.
|
|
273
|
+
if has_prev_positions {
|
|
274
|
+
// Mesh-local for the same reason the current-frame
|
|
275
|
+
// position is — the velocity pass reads these through
|
|
276
|
+
// the same rasterizer transform, so the space they live
|
|
277
|
+
// in has to match.
|
|
278
|
+
let prev_dq_blend = skin_blend_mesh_local(
|
|
279
|
+
inv_mesh_world,
|
|
280
|
+
prev_skin_matrices[skin_base + j0],
|
|
281
|
+
prev_skin_matrices[skin_base + j1],
|
|
282
|
+
prev_skin_matrices[skin_base + j2],
|
|
283
|
+
prev_skin_matrices[skin_base + j3],
|
|
284
|
+
weights,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
let p_prev_skinned = dual_quat_transform_point(prev_dq_blend, p);
|
|
288
|
+
|
|
289
|
+
let prev_addr = prev_pos_meshlet_base + v * 3u;
|
|
290
|
+
prev_positions[prev_addr] = bitcast<u32>(p_prev_skinned.x);
|
|
291
|
+
prev_positions[prev_addr + 1u] = bitcast<u32>(p_prev_skinned.y);
|
|
292
|
+
prev_positions[prev_addr + 2u] = bitcast<u32>(p_prev_skinned.z);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Normal: rotation only. Skip when the source compressed it
|
|
296
|
+
// down to a single shared slot — per-vertex writes to that
|
|
297
|
+
// slot would race, and the rotated direction would be
|
|
298
|
+
// mostly-meaningless anyway since every vertex would compute
|
|
299
|
+
// a different one.
|
|
300
|
+
//
|
|
301
|
+
// Mesh-local like the position, and left there: the
|
|
302
|
+
// rasterizer applies the inverse-transpose of mesh.world's
|
|
303
|
+
// 3x3 to it, which is the glTF normal transform and takes a
|
|
304
|
+
// mesh-local direction to world (see fragment_gbuffer.js).
|
|
305
|
+
if !normal_compressed {
|
|
306
|
+
let n_raw_addr = source_attr_section + normal_offset + v;
|
|
307
|
+
let n = decode_vertex_normal(meshlet_data[n_raw_addr]);
|
|
308
|
+
let n_skinned = normalize(dual_quat_transform_direction(dq_blend, n));
|
|
309
|
+
|
|
310
|
+
let clone_n_addr = clone_attr_section + normal_offset + v;
|
|
311
|
+
meshlet_data[clone_n_addr] = encode_vertex_normal(n_skinned);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Tangent: rotation only on the xyz; w (handedness) is a
|
|
315
|
+
// topological property, copied through unchanged.
|
|
316
|
+
if !tangent_compressed {
|
|
317
|
+
let t_raw_addr = source_attr_section + tangent_offset + v;
|
|
318
|
+
let t = decode_vertex_tangent(meshlet_data[t_raw_addr]);
|
|
319
|
+
let t_skinned_dir = normalize(dual_quat_transform_direction(dq_blend, t.xyz));
|
|
320
|
+
|
|
321
|
+
let clone_t_addr = clone_attr_section + tangent_offset + v;
|
|
322
|
+
meshlet_data[clone_t_addr] = encode_vertex_tangent(vec4f(t_skinned_dir, t.w));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
v = v + ${SKINNING_WORKGROUP_SIZE}u;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
`, [
|
|
329
|
+
MESHLET_METADATA_STRUCT.declaration_chunk,
|
|
330
|
+
SKINNING_BINDING_STRUCT.declaration_chunk,
|
|
331
|
+
chunk_meshlet_compute_attribute_section_offset,
|
|
332
|
+
chunk_skin_blend_mesh_local,
|
|
333
|
+
chunk_dual_quat_transform_point,
|
|
334
|
+
chunk_dual_quat_transform_direction,
|
|
335
|
+
chunk_decode_vertex_normal,
|
|
336
|
+
chunk_encode_vertex_normal,
|
|
337
|
+
chunk_decode_vertex_tangent,
|
|
338
|
+
chunk_encode_vertex_tangent,
|
|
339
|
+
chunk_scene_database_access,
|
|
340
|
+
chunk_mat4_inverse,
|
|
341
|
+
]
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Compute shader that deforms cloned meshlet vertex regions in place
|
|
346
|
+
* using the per-frame skin matrices the prep pass produced. One
|
|
347
|
+
* workgroup per (binding, meshlet) pair, dispatched against a flat
|
|
348
|
+
* `dispatch_lookup` table the host builds when bindings change.
|
|
349
|
+
*
|
|
350
|
+
* Skins position (full transform) plus normal and tangent (rotation
|
|
351
|
+
* only) per vertex via dual-quaternion blending — the blended DQ
|
|
352
|
+
* captures the four per-vertex joints' screw motions weighted by
|
|
353
|
+
* the per-vertex `SkinningWeights`, and applies cleanly to both
|
|
354
|
+
* positions and unit-direction attributes without the matrix-blend
|
|
355
|
+
* candy-wrapper artifacts that would otherwise show up in the
|
|
356
|
+
* normal-transformed lighting.
|
|
357
|
+
*
|
|
358
|
+
* Source bytes stay untouched — other instances pointed at the
|
|
359
|
+
* source render normally. Per-meshlet bounds aren't refreshed by
|
|
360
|
+
* this pass; meshlets that animate outside the source bounds may
|
|
361
|
+
* cull incorrectly until a future bounds-refresh phase lands.
|
|
362
|
+
*
|
|
363
|
+
* Dispatch via `shader.dispatch({ group_count_x: total_meshlet_count, ... })`
|
|
364
|
+
* where `total_meshlet_count` is the sum of `meshlet_count` across
|
|
365
|
+
* every registered binding. Skip when `total_meshlet_count === 0`.
|
|
366
|
+
*/
|
|
367
|
+
export const shader_meshlet_apply_skinning = ComputeShader.from({
|
|
368
|
+
label: "Skinning/MeshletApplySkinning",
|
|
369
|
+
resources,
|
|
370
|
+
body,
|
|
371
|
+
});
|