@woosh/meep-engine 3.17.2 → 3.18.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/editor/actions/concrete/ComponentAddAction.d.ts +1 -0
- package/editor/actions/concrete/ComponentAddAction.d.ts.map +1 -1
- package/editor/actions/concrete/ComponentAddAction.js +4 -0
- package/editor/actions/concrete/ComponentRemoveAction.d.ts +1 -0
- package/editor/actions/concrete/ComponentRemoveAction.d.ts.map +1 -1
- package/editor/actions/concrete/ComponentRemoveAction.js +4 -0
- package/editor/actions/concrete/EntityCreateAction.d.ts +1 -0
- package/editor/actions/concrete/EntityCreateAction.d.ts.map +1 -1
- package/editor/actions/concrete/EntityCreateAction.js +7 -0
- package/editor/actions/concrete/EntityRemoveAction.d.ts +1 -0
- package/editor/actions/concrete/EntityRemoveAction.d.ts.map +1 -1
- package/editor/actions/concrete/EntityRemoveAction.js +4 -0
- package/editor/actions/concrete/PatchTerrainHeightAction.d.ts +10 -0
- package/editor/actions/concrete/PatchTerrainHeightAction.d.ts.map +1 -1
- package/editor/actions/concrete/PatchTerrainHeightAction.js +107 -85
- package/editor/actions/concrete/TransformModifyAction.d.ts +1 -0
- package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
- package/editor/actions/concrete/TransformModifyAction.js +4 -0
- package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
- package/editor/particles/effect/ParticleEffectDocument.js +10 -6
- package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -1
- package/editor/particles/effect/ParticleReferenceSimulation.js +98 -4
- package/editor/particles/effect/make_starter_particle_effect.d.ts.map +1 -1
- package/editor/particles/effect/make_starter_particle_effect.js +0 -2
- package/editor/tools/engine/ToolEngine.d.ts +30 -0
- package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
- package/editor/tools/engine/ToolEngine.js +257 -188
- package/editor/view/ecs/EntityList.d.ts.map +1 -1
- package/editor/view/ecs/EntityList.js +6 -5
- package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
- package/editor/view/particles/effect/ParticleEmitterInspectorView.js +17 -9
- package/package.json +1 -1
- package/src/CODEC_LAYOUT_VERIFICATION.md +1 -2
- package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.d.ts +56 -0
- package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.d.ts.map +1 -0
- package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.js +73 -0
- package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.d.ts.map +1 -1
- package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +18 -63
- package/src/core/binary/meshopt/meshopt_encode_byte_group.d.ts +53 -0
- package/src/core/binary/meshopt/meshopt_encode_byte_group.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_byte_group.js +140 -0
- package/src/core/binary/meshopt/meshopt_encode_data_block.d.ts +32 -0
- package/src/core/binary/meshopt/meshopt_encode_data_block.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_data_block.js +77 -0
- package/src/core/binary/meshopt/meshopt_encode_index_sequence.d.ts +46 -0
- package/src/core/binary/meshopt/meshopt_encode_index_sequence.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_index_sequence.js +146 -0
- package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.d.ts +40 -0
- package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.js +700 -0
- package/src/core/binary/meshopt/meshopt_write_uint_var.d.ts +24 -0
- package/src/core/binary/meshopt/meshopt_write_uint_var.d.ts.map +1 -0
- package/src/core/binary/meshopt/meshopt_write_uint_var.js +39 -0
- package/src/core/math/random/seededRandom_Mulberry32.d.ts.map +1 -1
- package/src/core/math/random/seededRandom_Mulberry32.js +4 -2
- package/src/core/process/undo/Action.d.ts +19 -0
- package/src/core/process/undo/Action.d.ts.map +1 -1
- package/src/core/process/undo/Action.js +17 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +78 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -0
- package/src/engine/graphics3/GPUParticleEmitterSystem.js +310 -0
- package/src/engine/graphics3/GraphicsEngine.d.ts +17 -0
- package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
- package/src/engine/graphics3/GraphicsEngine.js +30 -10
- package/src/engine/graphics3/particles/particle_gpu_records.d.ts.map +1 -1
- package/src/engine/graphics3/particles/particle_gpu_records.js +0 -2
- package/src/engine/interpolation/InterpolationSystem.d.ts +7 -0
- package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
- package/src/engine/interpolation/InterpolationSystem.js +10 -0
- package/src/engine/network/NetworkSession.d.ts.map +1 -1
- package/src/engine/network/NetworkSession.js +14 -34
- package/src/engine/network/time/RenderPlayout.d.ts +76 -0
- package/src/engine/network/time/RenderPlayout.d.ts.map +1 -0
- package/src/engine/network/time/RenderPlayout.js +163 -0
- package/src/engine/network/transport/adapters/WebSocketTransport.d.ts.map +1 -1
- package/src/engine/network/transport/adapters/WebSocketTransport.js +11 -4
- package/src/engine/physics/ecs/PhysicsSystem.d.ts +4 -12
- package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
- package/src/engine/physics/ecs/PhysicsSystem.js +39 -13
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
- package/src/engine/sound/simulation/core/VolumeField.js +4 -1
- package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +23 -2
- package/src/shade/playground/particle_system/README.md +139 -139
- package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_prototype.js +0 -2
- package/src/shade/playground/particle_system/particle_scene.d.ts +6 -9
- package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
- package/src/shade/playground/particle_system/particle_scene.js +6 -13
- package/src/shade/playground/ssr_reprojection/README.md +103 -0
- package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.d.ts +17 -0
- package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.d.ts.map +1 -0
- package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.js +68 -0
- package/src/shade/playground/ssr_reprojection/index.html +79 -0
- package/src/shade/playground/ssr_reprojection/main.d.ts +2 -0
- package/src/shade/playground/ssr_reprojection/main.d.ts.map +1 -0
- package/src/shade/playground/ssr_reprojection/main.js +245 -0
- package/src/shade/playground/ssr_reprojection/scene.d.ts +18 -0
- package/src/shade/playground/ssr_reprojection/scene.d.ts.map +1 -0
- package/src/shade/playground/ssr_reprojection/scene.js +51 -0
- package/src/shade/renderer/Renderer.d.ts +2 -23
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +185 -218
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +5 -8
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +12 -12
- package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/GPUTypedTable.js +6 -0
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
- package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +7 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.d.ts +4 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.d.ts.map +1 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.js +51 -0
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.d.ts.map +1 -1
- package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.js +5 -35
- package/src/shade/renderer/particles/DESIGN.md +703 -514
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts +16 -4
- package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
- package/src/shade/renderer/particles/GPUParticleSystem.js +1189 -991
- package/src/shade/renderer/particles/ParticleConstants.d.ts +64 -0
- package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
- package/src/shade/renderer/particles/ParticleConstants.js +71 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts +57 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +181 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.d.ts +7 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.js +12 -0
- package/src/shade/renderer/particles/bounds/graph_particle_bounds.d.ts +33 -0
- package/src/shade/renderer/particles/bounds/graph_particle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/graph_particle_bounds.js +67 -0
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.d.ts +40 -0
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.d.ts.map +1 -0
- package/src/shade/renderer/particles/bounds/shader_particle_bounds.js +187 -0
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +15 -2
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
- package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +14 -8
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts +58 -4
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.js +79 -4
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts +23 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js +35 -2
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +4 -2
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +5 -8
- package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -1
- package/src/shade/renderer/particles/data/particle_emitter_record.js +0 -6
- package/src/shade/renderer/particles/graph_particles.d.ts +25 -3
- package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles.js +108 -31
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts +6 -4
- package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
- package/src/shade/renderer/particles/graph_particles_avboit.js +5 -5
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +5 -0
- package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/EmitterRegistry.js +27 -6
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +4 -0
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +6 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +35 -27
- package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
- package/src/shade/renderer/particles/runtime/ParticleEmitter.js +424 -425
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts +20 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts +4 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +38 -2
- package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.d.ts +22 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.js +42 -0
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.js +53 -22
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts.map +1 -1
- package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.js +213 -216
- package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts +50 -0
- package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts.map +1 -0
- package/src/shade/renderer/particles/shaders/shader_particle_render.js +1 -1
- package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.d.ts +36 -0
- package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.js +102 -0
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts +102 -0
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/graph_particle_warmup.js +329 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup.d.ts +112 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup.js +318 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts +35 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.js +108 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts +3 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.js +70 -0
- package/src/shade/renderer/particles/warmup/warmup_schedule.d.ts +62 -0
- package/src/shade/renderer/particles/warmup/warmup_schedule.d.ts.map +1 -0
- package/src/shade/renderer/particles/warmup/warmup_schedule.js +55 -0
- package/src/shade/renderer/postprocess/PostProcess.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/PostProcess.js +2 -1
- package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +286 -0
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts +57 -21
- package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/SSR.js +230 -129
- package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.d.ts +11 -0
- package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.js +6 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.js +32 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.js +41 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts +3 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.js +30 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts +4 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +70 -0
- package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts +21 -0
- package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.js +363 -0
- package/src/shade/renderer/postprocess/ssr/resolve/chunk_get_neighbour_weight.js +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.d.ts +2 -0
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.d.ts.map +1 -0
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.js +24 -0
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts +1 -6
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.js +16 -332
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts +1 -6
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.js +25 -358
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +0 -5
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +201 -276
- package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts +1 -0
- package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
- package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +167 -143
- package/src/shade/renderer/shader/graph/graph_create_buffer.d.ts +15 -0
- package/src/shade/renderer/shader/graph/graph_create_buffer.d.ts.map +1 -0
- package/src/shade/renderer/shader/graph/graph_create_buffer.js +20 -0
- package/src/shade/renderer/view/GPUViewContext.d.ts +3 -4
- package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
- package/src/shade/renderer/view/GPUViewContext.js +5 -5
- package/src/core/binary/meshopt/__meshopt_test_streams.d.ts +0 -24
- package/src/core/binary/meshopt/__meshopt_test_streams.d.ts.map +0 -1
- package/src/core/binary/meshopt/__meshopt_test_streams.js +0 -237
- package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts +0 -84
- package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts.map +0 -1
- package/src/core/geom/3d/atlas/atlas_test_fixtures.js +0 -266
- package/src/engine/asset/loaders/gltf_test_fixtures.d.ts +0 -97
- package/src/engine/asset/loaders/gltf_test_fixtures.d.ts.map +0 -1
- package/src/engine/asset/loaders/gltf_test_fixtures.js +0 -359
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts +0 -86
- package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +0 -1
- package/src/shade/renderer/animation/skin_test_fixtures.js +0 -250
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts +0 -16
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts.map +0 -1
- package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +0 -27
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts +0 -151
- package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +0 -1
- package/src/shade/renderer/particles/particle_test_fixtures.js +0 -229
- package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.js +0 -23
- package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.js +0 -457
|
@@ -80,7 +80,12 @@ a `0xa1` stream or a `COLOR` filter under the `EXT_` name is an invalid file and
|
|
|
80
80
|
| `meshopt_decode_filter_color.js` | filter 4, in place |
|
|
81
81
|
| `meshopt_decode.js` | dispatch: mode, then filter |
|
|
82
82
|
| `meshopt_read_uint_var.js` | bounded LEB128 over a `Uint8Array` |
|
|
83
|
-
| `
|
|
83
|
+
| `MESHOPT_VERTEX_BLOCK.js` | the attribute stream's block shape, shared by both sides |
|
|
84
|
+
| `meshopt_encode_byte_group.js` | one group, at a width it prices out of the four on offer |
|
|
85
|
+
| `meshopt_encode_data_block.js` | one data block: a width per group, then the header and the groups |
|
|
86
|
+
| `meshopt_encode_vertex_buffer.js` | mode 0, versions 0 and 1 |
|
|
87
|
+
| `meshopt_encode_index_sequence.js` | mode 2, choosing a baseline per index |
|
|
88
|
+
| `meshopt_write_uint_var.js` | LEB128 into a `Uint8Array` at an offset |
|
|
84
89
|
|
|
85
90
|
Signatures:
|
|
86
91
|
|
|
@@ -706,7 +711,23 @@ written down where they live rather than left to look like tested behaviour.
|
|
|
706
711
|
the module twice.
|
|
707
712
|
- The test-only encoder §5.3 put inline in a spec file became `__meshopt_test_streams.js`, because
|
|
708
713
|
the end-to-end glTF fixtures need the same streams. `**/__**.js` is already how meep's coverage
|
|
709
|
-
configuration spells "scaffolding"
|
|
714
|
+
configuration spells "scaffolding" — but the package's `files` list is not the coverage
|
|
715
|
+
configuration, and it drops `*.spec.js` and `__fixtures__/` and nothing else, so the file shipped
|
|
716
|
+
to every consumer of the engine.
|
|
717
|
+
|
|
718
|
+
Since replaced by a real encoder (`meshopt_encode_vertex_buffer.js` and
|
|
719
|
+
`meshopt_encode_index_sequence.js`), which belongs in the package on its own merits and leaves the
|
|
720
|
+
specs with nothing test-only to share. The encoder decides what the decoder was only ever told:
|
|
721
|
+
a width per group, a control mode per byte position, a mode per channel — each priced against the
|
|
722
|
+
alternatives rather than fixed, where the test-only encoder stored every group raw.
|
|
723
|
+
|
|
724
|
+
What it does not cover: mode 1, the triangle codec, whose encoder is a different algorithm
|
|
725
|
+
(vertex and edge FIFOs, and a rotation per triangle) rather than the inverse of a decode loop;
|
|
726
|
+
and the four filters, which are lossy and so are a quantisation choice rather than a codec one.
|
|
727
|
+
Neither had a consumer here, and neither is what the test-only encoder was doing. A triangle list
|
|
728
|
+
can go through mode 2 in the meantime, which is legal and decodes, just larger. There is no
|
|
729
|
+
`meshopt_encode.js` to match `meshopt_decode.js` for the same reason: a dispatcher over three
|
|
730
|
+
modes and five filters, two thirds of which throw, is worse than the two functions that exist.
|
|
710
731
|
- Three test vectors in the first draft were wrong and the implementation was right: a quaternion
|
|
711
732
|
component stored at full scale is `1/sqrt(2)` and not 1; a quaternion whose four components are
|
|
712
733
|
equal has its dropped component absorb all three of the others' rounding, so it is up to three and
|
|
@@ -1,139 +1,139 @@
|
|
|
1
|
-
# GPU particles, through AVBOIT — playground
|
|
2
|
-
|
|
3
|
-
The GPU particle path where it belongs: inside the engine's own `Renderer`, drawn by the AVBOIT
|
|
4
|
-
transparency pipeline as a side channel of the transparent meshes. The page owns a `Scene` and a
|
|
5
|
-
camera and nothing else — no frame graph, no pass, no pipeline, no camera buffer and no depth
|
|
6
|
-
texture. Two lines turn the particles on:
|
|
7
|
-
|
|
8
|
-
```js
|
|
9
|
-
renderer.transparency_mode = ShadeTransparencyMode.AVBOIT;
|
|
10
|
-
renderer.feature_particles_enabled = true;
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
`particle_prototype.js` — the worked-example `fire_effect` / `smoke_effect` / `register_campfire` —
|
|
14
|
-
lives here too, and is spec'd here rather than from the engine's own suite so that the dependency
|
|
15
|
-
runs playground → renderer and never back.
|
|
16
|
-
|
|
17
|
-
## Running it
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
npm run dev --workspace @woosh/meep-engine
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
then open <http://localhost:5173/src/shade/playground/particle_system/index.html>.
|
|
24
|
-
|
|
25
|
-
It has to be `http://localhost` rather than `file://`: WebGPU needs a secure context, and module
|
|
26
|
-
imports from `file://` are blocked by CORS anyway.
|
|
27
|
-
|
|
28
|
-
**It needs `subgroups` and immediate data**, both of which the renderer requires of any device it
|
|
29
|
-
accepts — the prefix scans behind the spawn counts, the coherence bucketing and the scene hierarchy
|
|
30
|
-
are built on subgroup ops, and every particle compute pass carries its per-dispatch state as an
|
|
31
|
-
immediate block. Chrome/Edge 113+ with immediates; Claude Code's in-app browser pane is **not** one
|
|
32
|
-
— it has reported no adapter at all since 2026-09-02, and no `immediate_address_space` in
|
|
33
|
-
`wgslLanguageFeatures` before that.
|
|
34
|
-
|
|
35
|
-
## What is on screen
|
|
36
|
-
|
|
37
|
-
A `Scene` with opaque geometry (a floor, a pillar, a block), **one transparent pane** across the
|
|
38
|
-
middle of the set, a sunlight, the default environment, and five emitters sharing one pool:
|
|
39
|
-
|
|
40
|
-
| emitter | blend | projection | what it is there to show |
|
|
41
|
-
|---|---|---|---|
|
|
42
|
-
| fire | additive | billboard | `fire_effect()` straight out of `particle_prototype.js` next door, at the foot of the pillar |
|
|
43
|
-
| smoke | alpha | billboard, soft-depth | a rising column that fades where it meets the pillar rather than cutting into it |
|
|
44
|
-
| sparks | additive | **stretched** | short-lived, gravity-fed, stretched along velocity — the one non-billboard projection |
|
|
45
|
-
| dust | alpha | billboard, soft-depth | a wide slow drift, so the floor gets a soft intersection |
|
|
46
|
-
| torch | additive | billboard | a child of a node that orbits — the emitter never moves, its parent does, and the GPU hierarchy pass composes the two |
|
|
47
|
-
|
|
48
|
-
The torch is the point of the scene-node design. Its record holds a row of the scene's
|
|
49
|
-
`transforms` table, not a matrix; the page writes its parent's translation once a frame the way it
|
|
50
|
-
would for any node, and the emitter's world position comes out of the same hierarchy pass that
|
|
51
|
-
places every mesh. An emitter parented to an animated joint works the same way, with nothing copied
|
|
52
|
-
through the CPU.
|
|
53
|
-
|
|
54
|
-
Four distinct compiled programs, so this is also where the simulation-order bucketing has something
|
|
55
|
-
to do: without it a simulate wave holds fire, smoke, spark and dust particles side by side, each
|
|
56
|
-
wanting different opcodes out of the interpreter's `switch`. Nothing on screen changes when it is on
|
|
57
|
-
— it is a coherence change, not a visual one — which is why it is pinned by specs rather than by
|
|
58
|
-
looking.
|
|
59
|
-
|
|
60
|
-
## The property the page exists to demonstrate
|
|
61
|
-
|
|
62
|
-
**Particles and transparent surfaces composite against each other with no order between them.**
|
|
63
|
-
Orbit around the pane. From in front, the particles behind it are dimmed by it; from behind, the
|
|
64
|
-
pane is dimmed by the smoke in front of it — and neither was sorted against the other, because both
|
|
65
|
-
wrote their extinction into the same voxel volume and both were resolved out of the same
|
|
66
|
-
accumulators. That is what a side channel buys, and what a blended pass bolted on after the
|
|
67
|
-
transparency pipeline cannot do at any price.
|
|
68
|
-
|
|
69
|
-
Two more things are visible from the same seat:
|
|
70
|
-
|
|
71
|
-
- **the additive emitters do not darken what is behind them.** Fire, sparks and the torch write the
|
|
72
|
-
emission accumulator, which the resolve adds outside the coverage normalization, so they light
|
|
73
|
-
the frame without taking coverage from it.
|
|
74
|
-
- **switch the mode to MBOIT and the particles vanish while the pane stays.** MBOIT is the legacy
|
|
75
|
-
path and has no particle channel. The simulation keeps running — turn AVBOIT back on and the
|
|
76
|
-
effects are where they would have been, not restarted — which is the honest way to see which
|
|
77
|
-
pipeline the particles now live in.
|
|
78
|
-
|
|
79
|
-
Also worth looking at, from the path itself:
|
|
80
|
-
|
|
81
|
-
- particles disappearing behind the pillar as the camera orbits — the depth test, read-only, on the
|
|
82
|
-
same buffer the soft fade samples,
|
|
83
|
-
- smoke and dust softening where they touch a surface instead of showing a hard intersection line,
|
|
84
|
-
- sparks leaning into their own velocity — `EMITTER_PROJECTION.STRETCHED`,
|
|
85
|
-
- the torch's trail following its carrier.
|
|
86
|
-
|
|
87
|
-
The frame is GPU-driven end to end, spawning included. The host walks no emitters and counts no
|
|
88
|
-
particles: the emitter tick pass integrates every emitter's rate on the GPU, a prefix scan of its
|
|
89
|
-
counts sizes the emit dispatch, and `shader_particle_build_indirect` writes the simulate dispatch
|
|
90
|
-
args and the draw args. The "Burst 4000 sparks" button is the one thing the CPU asks for, and it
|
|
91
|
-
asks through the same path — a command list a pass adds to that emitter's pending spawns.
|
|
92
|
-
|
|
93
|
-
The overlay shows fps and, every 30 frames, the alive count, the free-list length and the two
|
|
94
|
-
indirect arg buffers. Those are read back **to look at**, on a command buffer of their own after the
|
|
95
|
-
renderer's. Nothing on the CPU sizes a pass.
|
|
96
|
-
|
|
97
|
-
## Bounds spheres matter here
|
|
98
|
-
|
|
99
|
-
Every emitter carries one, and under AVBOIT it is not only the culling test it used to be: the
|
|
100
|
-
occupancy pass marks the depth slices an emitter may occlude in from that sphere, once per emitter
|
|
101
|
-
rather than once per particle. A sphere that does not hold its effect leaves particles occluding at
|
|
102
|
-
slightly the wrong depth. `PARTICLE_EMITTER_MOTION` in `particle_scene.js` is the motion each effect
|
|
103
|
-
is built from, and `particle_scene.spec.js` checks the spheres against it.
|
|
104
|
-
|
|
105
|
-
They are sized to hold where the effect actually goes, not the last corner its jitter could reach:
|
|
106
|
-
a sphere big enough for the worst case would mark most of the depth range and cost the adaptive
|
|
107
|
-
slicing the very thing it is for.
|
|
108
|
-
|
|
109
|
-
## Verifying it
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npx vitest run src/shade/playground/particle_system
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
`particle_scene.js` holds the scene and the configuration with every reference to the page removed,
|
|
116
|
-
so the spec builds the real scene — meshlets, materials, emitters — with no device, and checks what
|
|
117
|
-
is the page's to get wrong: the emitters are the scene's nodes, the torch hangs off the carrier, the
|
|
118
|
-
pane is transparent while the rest is opaque,
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
What a particle *frame* encodes is not this page's business any more. It is pinned next to the code,
|
|
122
|
-
against `SoftwareGPUDevice`, in `renderer/particles/graph_particles.spec.js` (the simulation, pass
|
|
123
|
-
by pass, and every dispatch sized by GPU-written args) and `graph_particles_avboit.spec.js` (the
|
|
124
|
-
three side-channel passes, their attachments and their indirect draws).
|
|
125
|
-
|
|
126
|
-
The picture is the browser's job; no spec here can see it.
|
|
127
|
-
|
|
128
|
-
### Stepping frames by hand
|
|
129
|
-
|
|
130
|
-
Add `?debug` to the URL. `requestAnimationFrame` is then not used at all, and `window.__step(n)`
|
|
131
|
-
runs exactly `n` frames and returns the frame counter — which is what makes the loop drivable in a
|
|
132
|
-
hidden or headless tab, where rAF is suspended. `window.__stats` carries the last counters snapshot,
|
|
133
|
-
and `camera_state()` / `apply_camera(...)` copy a view between sessions.
|
|
134
|
-
|
|
135
|
-
## What this page does not cover
|
|
136
|
-
|
|
137
|
-
Lighting is the unlit hook — `chunk_particle_lighting_unlit`, not the shading-chunk-backed one a
|
|
138
|
-
lit-emitter variant would swap in. No emitter opts into simulation culling, so the frustum test in
|
|
139
|
-
the tick pass is never taken here; the
|
|
1
|
+
# GPU particles, through AVBOIT — playground
|
|
2
|
+
|
|
3
|
+
The GPU particle path where it belongs: inside the engine's own `Renderer`, drawn by the AVBOIT
|
|
4
|
+
transparency pipeline as a side channel of the transparent meshes. The page owns a `Scene` and a
|
|
5
|
+
camera and nothing else — no frame graph, no pass, no pipeline, no camera buffer and no depth
|
|
6
|
+
texture. Two lines turn the particles on:
|
|
7
|
+
|
|
8
|
+
```js
|
|
9
|
+
renderer.transparency_mode = ShadeTransparencyMode.AVBOIT;
|
|
10
|
+
renderer.feature_particles_enabled = true;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`particle_prototype.js` — the worked-example `fire_effect` / `smoke_effect` / `register_campfire` —
|
|
14
|
+
lives here too, and is spec'd here rather than from the engine's own suite so that the dependency
|
|
15
|
+
runs playground → renderer and never back.
|
|
16
|
+
|
|
17
|
+
## Running it
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm run dev --workspace @woosh/meep-engine
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
then open <http://localhost:5173/src/shade/playground/particle_system/index.html>.
|
|
24
|
+
|
|
25
|
+
It has to be `http://localhost` rather than `file://`: WebGPU needs a secure context, and module
|
|
26
|
+
imports from `file://` are blocked by CORS anyway.
|
|
27
|
+
|
|
28
|
+
**It needs `subgroups` and immediate data**, both of which the renderer requires of any device it
|
|
29
|
+
accepts — the prefix scans behind the spawn counts, the coherence bucketing and the scene hierarchy
|
|
30
|
+
are built on subgroup ops, and every particle compute pass carries its per-dispatch state as an
|
|
31
|
+
immediate block. Chrome/Edge 113+ with immediates; Claude Code's in-app browser pane is **not** one
|
|
32
|
+
— it has reported no adapter at all since 2026-09-02, and no `immediate_address_space` in
|
|
33
|
+
`wgslLanguageFeatures` before that.
|
|
34
|
+
|
|
35
|
+
## What is on screen
|
|
36
|
+
|
|
37
|
+
A `Scene` with opaque geometry (a floor, a pillar, a block), **one transparent pane** across the
|
|
38
|
+
middle of the set, a sunlight, the default environment, and five emitters sharing one pool:
|
|
39
|
+
|
|
40
|
+
| emitter | blend | projection | what it is there to show |
|
|
41
|
+
|---|---|---|---|
|
|
42
|
+
| fire | additive | billboard | `fire_effect()` straight out of `particle_prototype.js` next door, at the foot of the pillar |
|
|
43
|
+
| smoke | alpha | billboard, soft-depth | a rising column that fades where it meets the pillar rather than cutting into it |
|
|
44
|
+
| sparks | additive | **stretched** | short-lived, gravity-fed, stretched along velocity — the one non-billboard projection |
|
|
45
|
+
| dust | alpha | billboard, soft-depth | a wide slow drift, so the floor gets a soft intersection |
|
|
46
|
+
| torch | additive | billboard | a child of a node that orbits — the emitter never moves, its parent does, and the GPU hierarchy pass composes the two |
|
|
47
|
+
|
|
48
|
+
The torch is the point of the scene-node design. Its record holds a row of the scene's
|
|
49
|
+
`transforms` table, not a matrix; the page writes its parent's translation once a frame the way it
|
|
50
|
+
would for any node, and the emitter's world position comes out of the same hierarchy pass that
|
|
51
|
+
places every mesh. An emitter parented to an animated joint works the same way, with nothing copied
|
|
52
|
+
through the CPU.
|
|
53
|
+
|
|
54
|
+
Four distinct compiled programs, so this is also where the simulation-order bucketing has something
|
|
55
|
+
to do: without it a simulate wave holds fire, smoke, spark and dust particles side by side, each
|
|
56
|
+
wanting different opcodes out of the interpreter's `switch`. Nothing on screen changes when it is on
|
|
57
|
+
— it is a coherence change, not a visual one — which is why it is pinned by specs rather than by
|
|
58
|
+
looking.
|
|
59
|
+
|
|
60
|
+
## The property the page exists to demonstrate
|
|
61
|
+
|
|
62
|
+
**Particles and transparent surfaces composite against each other with no order between them.**
|
|
63
|
+
Orbit around the pane. From in front, the particles behind it are dimmed by it; from behind, the
|
|
64
|
+
pane is dimmed by the smoke in front of it — and neither was sorted against the other, because both
|
|
65
|
+
wrote their extinction into the same voxel volume and both were resolved out of the same
|
|
66
|
+
accumulators. That is what a side channel buys, and what a blended pass bolted on after the
|
|
67
|
+
transparency pipeline cannot do at any price.
|
|
68
|
+
|
|
69
|
+
Two more things are visible from the same seat:
|
|
70
|
+
|
|
71
|
+
- **the additive emitters do not darken what is behind them.** Fire, sparks and the torch write the
|
|
72
|
+
emission accumulator, which the resolve adds outside the coverage normalization, so they light
|
|
73
|
+
the frame without taking coverage from it.
|
|
74
|
+
- **switch the mode to MBOIT and the particles vanish while the pane stays.** MBOIT is the legacy
|
|
75
|
+
path and has no particle channel. The simulation keeps running — turn AVBOIT back on and the
|
|
76
|
+
effects are where they would have been, not restarted — which is the honest way to see which
|
|
77
|
+
pipeline the particles now live in.
|
|
78
|
+
|
|
79
|
+
Also worth looking at, from the path itself:
|
|
80
|
+
|
|
81
|
+
- particles disappearing behind the pillar as the camera orbits — the depth test, read-only, on the
|
|
82
|
+
same buffer the soft fade samples,
|
|
83
|
+
- smoke and dust softening where they touch a surface instead of showing a hard intersection line,
|
|
84
|
+
- sparks leaning into their own velocity — `EMITTER_PROJECTION.STRETCHED`,
|
|
85
|
+
- the torch's trail following its carrier.
|
|
86
|
+
|
|
87
|
+
The frame is GPU-driven end to end, spawning included. The host walks no emitters and counts no
|
|
88
|
+
particles: the emitter tick pass integrates every emitter's rate on the GPU, a prefix scan of its
|
|
89
|
+
counts sizes the emit dispatch, and `shader_particle_build_indirect` writes the simulate dispatch
|
|
90
|
+
args and the draw args. The "Burst 4000 sparks" button is the one thing the CPU asks for, and it
|
|
91
|
+
asks through the same path — a command list a pass adds to that emitter's pending spawns.
|
|
92
|
+
|
|
93
|
+
The overlay shows fps and, every 30 frames, the alive count, the free-list length and the two
|
|
94
|
+
indirect arg buffers. Those are read back **to look at**, on a command buffer of their own after the
|
|
95
|
+
renderer's. Nothing on the CPU sizes a pass.
|
|
96
|
+
|
|
97
|
+
## Bounds spheres matter here
|
|
98
|
+
|
|
99
|
+
Every emitter carries one, and under AVBOIT it is not only the culling test it used to be: the
|
|
100
|
+
occupancy pass marks the depth slices an emitter may occlude in from that sphere, once per emitter
|
|
101
|
+
rather than once per particle. A sphere that does not hold its effect leaves particles occluding at
|
|
102
|
+
slightly the wrong depth. `PARTICLE_EMITTER_MOTION` in `particle_scene.js` is the motion each effect
|
|
103
|
+
is built from, and `particle_scene.spec.js` checks the spheres against it.
|
|
104
|
+
|
|
105
|
+
They are sized to hold where the effect actually goes, not the last corner its jitter could reach:
|
|
106
|
+
a sphere big enough for the worst case would mark most of the depth range and cost the adaptive
|
|
107
|
+
slicing the very thing it is for.
|
|
108
|
+
|
|
109
|
+
## Verifying it
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx vitest run src/shade/playground/particle_system
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`particle_scene.js` holds the scene and the configuration with every reference to the page removed,
|
|
116
|
+
so the spec builds the real scene — meshlets, materials, emitters — with no device, and checks what
|
|
117
|
+
is the page's to get wrong: the emitters are the scene's nodes, the torch hangs off the carrier, the
|
|
118
|
+
pane is transparent while the rest is opaque, and `configure_particles` turns the feature on, selects
|
|
119
|
+
AVBOIT and hands the system its sprite.
|
|
120
|
+
|
|
121
|
+
What a particle *frame* encodes is not this page's business any more. It is pinned next to the code,
|
|
122
|
+
against `SoftwareGPUDevice`, in `renderer/particles/graph_particles.spec.js` (the simulation, pass
|
|
123
|
+
by pass, and every dispatch sized by GPU-written args) and `graph_particles_avboit.spec.js` (the
|
|
124
|
+
three side-channel passes, their attachments and their indirect draws).
|
|
125
|
+
|
|
126
|
+
The picture is the browser's job; no spec here can see it.
|
|
127
|
+
|
|
128
|
+
### Stepping frames by hand
|
|
129
|
+
|
|
130
|
+
Add `?debug` to the URL. `requestAnimationFrame` is then not used at all, and `window.__step(n)`
|
|
131
|
+
runs exactly `n` frames and returns the frame counter — which is what makes the loop drivable in a
|
|
132
|
+
hidden or headless tab, where rAF is suspended. `window.__stats` carries the last counters snapshot,
|
|
133
|
+
and `camera_state()` / `apply_camera(...)` copy a view between sessions.
|
|
134
|
+
|
|
135
|
+
## What this page does not cover
|
|
136
|
+
|
|
137
|
+
Lighting is the unlit hook — `chunk_particle_lighting_unlit`, not the shading-chunk-backed one a
|
|
138
|
+
lit-emitter variant would swap in. No emitter opts into simulation culling, so the frustum test in
|
|
139
|
+
the tick pass is never taken here; the measured boxes are read by the occupancy pass all the same.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"particle_prototype.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/particle_system/particle_prototype.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;GASG;AACH;IAL4B,UAAU,GAA3B,MAAM;IACW,WAAW,GAA5B,MAAM;IACW,eAAe,GAAhC,MAAM;;YACK,cAAc;YAAU,MAAM;aAAW,MAAM;
|
|
1
|
+
{"version":3,"file":"particle_prototype.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/particle_system/particle_prototype.js"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;GASG;AACH;IAL4B,UAAU,GAA3B,MAAM;IACW,WAAW,GAA5B,MAAM;IACW,eAAe,GAAhC,MAAM;;YACK,cAAc;YAAU,MAAM;aAAW,MAAM;EA+GpE;AAED;;;;GAIG;AACH;YAFsB,cAAc;YAAU,MAAM;aAAW,MAAM;EAYpE;AAED;;;;;;;;;;;;;GAaG;AACH,yCANW,OAAO,+BAA+B,EAAE,KAAK;IAEa,IAAI,GAA9D,OAAO,gCAAgC,EAAE,MAAM;;UAEtC,eAAe;WAAS,eAAe;EAe1D;+BAzL8B,mDAAmD;gCAElD,qDAAqD"}
|
|
@@ -139,7 +139,6 @@ export function fire_effect({ size_curve, alpha_curve, flipbook_frames = 16 } =
|
|
|
139
139
|
spawn_rate: 120,
|
|
140
140
|
flags: { blend: EMITTER_BLEND.ADDITIVE, projection: EMITTER_PROJECTION.BILLBOARD },
|
|
141
141
|
render: { position: "position", size: "size", color: "color", frame: "frame" },
|
|
142
|
-
bounds: { center: [0, 1.5, 0], radius: 2.5 },
|
|
143
142
|
// No texture, so the flipbook runs over the default atlas — one white texel, read as a
|
|
144
143
|
// 4x4 grid of it. A real effect names an image and the system packs it.
|
|
145
144
|
flipbook: [4, 4],
|
|
@@ -160,7 +159,6 @@ export function smoke_effect() {
|
|
|
160
159
|
spawn_rate: 25,
|
|
161
160
|
flags: { blend: EMITTER_BLEND.ALPHA, projection: EMITTER_PROJECTION.BILLBOARD, soft_depth: true, lighting: true, sort: true },
|
|
162
161
|
render: { position: "position", size: "size", color: "color", frame: "frame" },
|
|
163
|
-
bounds: { center: [0, 3, 0], radius: 5 },
|
|
164
162
|
flipbook: [1, 1],
|
|
165
163
|
};
|
|
166
164
|
return { layout, effect, emitter };
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
* The five emitters, and the node one of them rides. Pure data: no device, no scene, nothing
|
|
3
3
|
* uploaded — {@link build_particle_scene} is what places them.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* and cost the adaptive slicing the very thing it is for.
|
|
5
|
+
* None of them carries bounds. The box the culling and occupancy passes use is measured from the
|
|
6
|
+
* live particles on the GPU and published into the emitter state table every tick, so an effect
|
|
7
|
+
* that travels further than anyone expected marks the depth slices it actually reaches — which is
|
|
8
|
+
* what these emitters, with their long-lived jittered plumes, would otherwise be a standing
|
|
9
|
+
* invitation to get wrong.
|
|
11
10
|
*
|
|
12
11
|
* @returns {{fire: ParticleEmitter, smoke: ParticleEmitter, sparks: ParticleEmitter, dust: ParticleEmitter, torch: ParticleEmitter, orbit: Node3D}}
|
|
13
12
|
*/
|
|
@@ -121,9 +120,7 @@ export const PARTICLE_STATS_BYTES: 64;
|
|
|
121
120
|
*/
|
|
122
121
|
/**
|
|
123
122
|
* What each of the four parametric emitters emits, apart from the fire, whose effect is the worked
|
|
124
|
-
* example next door. Exported
|
|
125
|
-
* and a spec can check that they still hold: an emitter that travels further than its sphere
|
|
126
|
-
* occludes at the wrong depth now that the sphere is the occupancy hint.
|
|
123
|
+
* example next door. Exported so the page can show what each emitter is doing.
|
|
127
124
|
*
|
|
128
125
|
* @type {Object<string, {velocity: number[], velocityJitter: number, posJitter: number, accel: number, life: number[], size: number, color: number[]}>}
|
|
129
126
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"particle_scene.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/particle_system/particle_scene.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"particle_scene.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/particle_system/particle_scene.js"],"names":[],"mappings":"AAwFA;;;;;;;;;;;GAWG;AACH;UAFoB,eAAe;WAAS,eAAe;YAAU,eAAe;UAAQ,eAAe;WAAS,eAAe;WAAS,MAAM;EA+DjJ;AAED;;;;;;;GAOG;AACH;WAF6B,KAAK;cAAY,2CAAoC;;eAAkB,IAAI;gBAAU,IAAI;eAAS,IAAI;cAAQ,IAAI;;GAuD9I;AAED;;;;;;;;;;GAUG;AACH,8CAJW,OAAO,4BAA4B,EAAE,QAAQ,SAC7C,KAAK,GACH,OAAO,+CAA+C,EAAE,iBAAiB,CAmBrF;AAED;;;;;;GAMG;AACH,iDAHW,2CAAoC,QACpC,MAAM,QAWhB;AAED;;;;;;;GAOG;AACH,0CAJW,OAAO,+CAA+C,EAAE,iBAAiB,4DAUnF;AAED;;;;;GAKG;AACH,kCAHW,WAAW,GACT,kBAAkB,CAgB9B;AAED;;;;;GAKG;AACH,mEA4BC;AA5TD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,0FAA0F;AAC1F,sCAAuC;AAKvC;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH;QAFiB,MAAM,GAAE;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAC;EAOpJ;;;;;WAtBY,MAAM;;;;UACN,MAAM;;;;kBACN,MAAM;;;;;cACN,MAAM;;;;;gBAEN,MAAM;;;;cAEN,MAAM,EAAE;;;;UACR,MAAM,EAAE;;gCAjDU,qDAAqD;uBAE9D,gCAAgC;sBACjC,+BAA+B;qBAFhC,8BAA8B"}
|
|
@@ -75,9 +75,7 @@ const STATS_OFFSET_DRAW_ARGS = 48;
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* What each of the four parametric emitters emits, apart from the fire, whose effect is the worked
|
|
78
|
-
* example next door. Exported
|
|
79
|
-
* and a spec can check that they still hold: an emitter that travels further than its sphere
|
|
80
|
-
* occludes at the wrong depth now that the sphere is the occupancy hint.
|
|
78
|
+
* example next door. Exported so the page can show what each emitter is doing.
|
|
81
79
|
*
|
|
82
80
|
* @type {Object<string, {velocity: number[], velocityJitter: number, posJitter: number, accel: number, life: number[], size: number, color: number[]}>}
|
|
83
81
|
*/
|
|
@@ -92,12 +90,11 @@ export const PARTICLE_EMITTER_MOTION = {
|
|
|
92
90
|
* The five emitters, and the node one of them rides. Pure data: no device, no scene, nothing
|
|
93
91
|
* uploaded — {@link build_particle_scene} is what places them.
|
|
94
92
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* and cost the adaptive slicing the very thing it is for.
|
|
93
|
+
* None of them carries bounds. The box the culling and occupancy passes use is measured from the
|
|
94
|
+
* live particles on the GPU and published into the emitter state table every tick, so an effect
|
|
95
|
+
* that travels further than anyone expected marks the depth slices it actually reaches — which is
|
|
96
|
+
* what these emitters, with their long-lived jittered plumes, would otherwise be a standing
|
|
97
|
+
* invitation to get wrong.
|
|
101
98
|
*
|
|
102
99
|
* @returns {{fire: ParticleEmitter, smoke: ParticleEmitter, sparks: ParticleEmitter, dust: ParticleEmitter, torch: ParticleEmitter, orbit: Node3D}}
|
|
103
100
|
*/
|
|
@@ -120,7 +117,6 @@ export function create_particle_emitters() {
|
|
|
120
117
|
flags: { blend: EMITTER_BLEND.ALPHA, projection: EMITTER_PROJECTION.BILLBOARD, soft_depth: true },
|
|
121
118
|
render: { position: "position", size: "size", color: "color" },
|
|
122
119
|
// rising at 1.3 m/s for 3 to 5 s, with the buoyancy adding a couple of metres on top
|
|
123
|
-
bounds: { center: [0, 4, 0], radius: 7 },
|
|
124
120
|
position: [-1, 2.5, 0],
|
|
125
121
|
});
|
|
126
122
|
|
|
@@ -132,7 +128,6 @@ export function create_particle_emitters() {
|
|
|
132
128
|
flags: { blend: EMITTER_BLEND.ADDITIVE, projection: EMITTER_PROJECTION.STRETCHED },
|
|
133
129
|
render: { position: "position", size: "size", color: "color", velocity: "velocity" },
|
|
134
130
|
// thrown a few metres up and out, and pulled back down inside a second
|
|
135
|
-
bounds: { center: [0, 0.5, 0], radius: 5 },
|
|
136
131
|
position: [2, 1.9, -1],
|
|
137
132
|
});
|
|
138
133
|
|
|
@@ -144,7 +139,6 @@ export function create_particle_emitters() {
|
|
|
144
139
|
flags: { blend: EMITTER_BLEND.ALPHA, projection: EMITTER_PROJECTION.BILLBOARD, soft_depth: true },
|
|
145
140
|
render: { position: "position", size: "size", color: "color" },
|
|
146
141
|
// emitted across a 6 m disk, then drifting slowly for up to 7 s
|
|
147
|
-
bounds: { center: [1.5, 1, 0.5], radius: 10 },
|
|
148
142
|
position: [0, 0.3, 0],
|
|
149
143
|
});
|
|
150
144
|
|
|
@@ -159,7 +153,6 @@ export function create_particle_emitters() {
|
|
|
159
153
|
spawn_rate: 200,
|
|
160
154
|
flags: { blend: EMITTER_BLEND.ADDITIVE, projection: EMITTER_PROJECTION.BILLBOARD },
|
|
161
155
|
render: { position: "position", size: "size", color: "color" },
|
|
162
|
-
bounds: { center: [0, 1, 0], radius: 2.5 },
|
|
163
156
|
position: [4, 1.2, 0],
|
|
164
157
|
});
|
|
165
158
|
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# SSR reflection lab
|
|
2
|
+
|
|
3
|
+
Run `npm run dev` from `packages/meep-engine`, then open
|
|
4
|
+
<http://localhost:5173/src/shade/playground/ssr_reprojection/>.
|
|
5
|
+
The page uses the actual WebGPU renderer and production SSR shaders. Geometry and
|
|
6
|
+
environment lighting are procedural; only the engine's bundled lookup/noise textures
|
|
7
|
+
are loaded. The browser must meet the renderer's WebGPU feature requirements.
|
|
8
|
+
|
|
9
|
+
## Five-minute check
|
|
10
|
+
|
|
11
|
+
1. Start with **Lateral camera**, **Parallax**, roughness **0.03**, and final TAA off.
|
|
12
|
+
Watch the reflected coloured blocks move relative to the floor grid. Solid crosses
|
|
13
|
+
mark ideal planar-mirror image centres. Dashed crosses are fixed floor positions,
|
|
14
|
+
aligned at the start of the path. Their separation illustrates surface sticking.
|
|
15
|
+
2. Select **Surface only**, then **No temporal history**. Switching mode restarts
|
|
16
|
+
the path and resets histories. Compare edge lag and noise at the same part of the
|
|
17
|
+
repeatable path. Surface only uses the new validation/blending, not the old shader.
|
|
18
|
+
3. Try **Forward camera** and **Orbit camera**, then raise roughness through 0.2.
|
|
19
|
+
The virtual candidate is deliberately restricted to smooth, coherent reflections.
|
|
20
|
+
4. Use **Moving object** and **Occluder** to expose residual trails and disocclusion.
|
|
21
|
+
Moving reflected geometry is an acknowledged limitation. Missing off-screen or
|
|
22
|
+
hidden source geometry is also an SSR limitation, not necessarily a reprojection bug.
|
|
23
|
+
5. Press **Camera cut**, **Reset reflection history**, and change resolution.
|
|
24
|
+
Old reflections should not persist. **SSR off** is a useful control proving that
|
|
25
|
+
the visible reflection comes from SSR rather than the flat environment.
|
|
26
|
+
|
|
27
|
+
The guides reflect physical object centres across y=0 independently of the temporal
|
|
28
|
+
ray-extension formula. They are geometry guides, not colour references: visible
|
|
29
|
+
front faces, occlusion and roughness do not necessarily centre on the marker. The
|
|
30
|
+
floor is planar and its top is exactly y=0. Grid strips slightly above it are visible
|
|
31
|
+
surface landmarks. Guides do not perform occlusion testing.
|
|
32
|
+
|
|
33
|
+
The page fixes exposure and disables bloom, sharpening, ambient occlusion and motion
|
|
34
|
+
blur. Final-image TAA defaults off to expose SSR's own temporal behaviour. Pause
|
|
35
|
+
motion leaves temporal sampling running; **Still camera / noise** does the same at
|
|
36
|
+
the initial view. **Step frame** advances the motion by 1/60 second. For exact manual
|
|
37
|
+
frame counts, append `?manual` to the URL: no animation loop is scheduled, and the
|
|
38
|
+
step button submits one frame at a time. From the browser console,
|
|
39
|
+
`ssr_lab.step(60)` advances 60 deterministic frames at 1/60 second.
|
|
40
|
+
|
|
41
|
+
**Validate GPU shaders** compiles trace, both resolves, both spatial variants, and
|
|
42
|
+
temporal reprojection on the live GPU. This checks native WGSL compilation, while
|
|
43
|
+
running the playground also exercises IBL pipeline creation and frame-graph wiring.
|
|
44
|
+
The page reports and stops on uncaptured GPU errors. LPV composition is checked but
|
|
45
|
+
the procedural scene uses IBL, so this does not test LPV atlas content or lighting.
|
|
46
|
+
|
|
47
|
+
Production integration is small: `renderer.ssr.reprojection_mode` selects
|
|
48
|
+
`SSRReprojectionMode.Parallax`, `.Surface`, or `.CurrentFrame`. Changing it invalidates
|
|
49
|
+
SSR history. Default rendering continues to use Parallax.
|
|
50
|
+
|
|
51
|
+
Do not use frame rate on this page as an SSR-only GPU timing measurement. It includes
|
|
52
|
+
the entire renderer, the browser, and the overlay. Profile the actual passes before
|
|
53
|
+
making performance claims. HMR is disabled by the playground Vite config; reload
|
|
54
|
+
the browser explicitly after source edits.
|
|
55
|
+
|
|
56
|
+
## Validation performed
|
|
57
|
+
|
|
58
|
+
The page was exercised on the native NVIDIA adapter: IBL rendering, all four
|
|
59
|
+
comparison modes, a moving target and occluder, camera cut, resolution change,
|
|
60
|
+
and final TAA toggle. The SSR-off control visibly removes the reflections.
|
|
61
|
+
All six SSR shader modules compile on that device, and the 14 SSR regression
|
|
62
|
+
tests pass. Declaration emission and consumer API pins pass; the existing
|
|
63
|
+
repository type-error budget is still exceeded (899 errors versus 897).
|
|
64
|
+
|
|
65
|
+
## Spatial denoising
|
|
66
|
+
|
|
67
|
+
The **Spatial denoising** checkbox defaults on. It toggles one spatial pass before
|
|
68
|
+
reprojection and two afterward; the chosen reprojection mode stays unchanged.
|
|
69
|
+
Changing this setting resets SSR history so filtered and unfiltered inputs are not
|
|
70
|
+
mixed across the transition. The runtime setting is
|
|
71
|
+
`renderer.ssr.spatial_denoising_enabled`. With the box unchecked and **No temporal
|
|
72
|
+
history** selected, the output comes directly from resolve. Resolve still borrows
|
|
73
|
+
neighbouring ray hits and uses roughness-dependent radiance filtering; this switch
|
|
74
|
+
does not turn the resolve stage into a raw one-ray-per-pixel view.
|
|
75
|
+
|
|
76
|
+
## Measuring actual filter effectiveness
|
|
77
|
+
|
|
78
|
+
**Measure spatial filter** records the first spatial pass's input and output in
|
|
79
|
+
one GPU frame. It reports the fraction of upward-facing mirror pixels whose maximum
|
|
80
|
+
RGB-channel difference exceeds 0.001, mean/max difference in linear HDR radiance,
|
|
81
|
+
and the fraction whose input variance is effectively zero. It is an effect check,
|
|
82
|
+
not an image-quality score or an SSR performance benchmark. Readback runs only on
|
|
83
|
+
request; its temporary texture is released afterward. Ordinary playback has no
|
|
84
|
+
probe passes. SSR must be enabled. With spatial filtering off, the bypass is compared
|
|
85
|
+
with itself and the measured difference is zero.
|
|
86
|
+
|
|
87
|
+
This probe exposed a filter failure on 2026-09-08: roughly 98% of this scene's mirror
|
|
88
|
+
pixels had resolve variance ≤1e-6. The old first pass changed radiance by just 0.00137
|
|
89
|
+
on average; the sparse noisy samples were being rejected as colour edges. Resolve's
|
|
90
|
+
within-pixel ray variance is not sufficient evidence of low spatial noise when only
|
|
91
|
+
one hit supplies radiance or the hit confidence switches between SSR and fallback.
|
|
92
|
+
|
|
93
|
+
The first pass now bootstraps uncertainty from a Gaussian-weighted 3×3 luminance
|
|
94
|
+
neighbourhood, using the existing geometry weights, and carries that uncertainty
|
|
95
|
+
through the subsequent spatial passes. It does not bootstrap again at larger radii.
|
|
96
|
+
A representative corrected GPU measurement gave mean change 0.00552 with filtering
|
|
97
|
+
on, and 0 with it off. These are separate frames, not a matched-noise quality metric.
|
|
98
|
+
The native images visibly differ, and an emulator regression verifies reduced
|
|
99
|
+
checker noise with zero input variance, unchanged constant colour (within float
|
|
100
|
+
precision), and rejection across opposite normals. The full SSR suite has 16 tests.
|
|
101
|
+
|
|
102
|
+
Spatial filtering trades sharpness for noise reduction, including at reflected
|
|
103
|
+
colour edges; these guides and tests do not establish an optimal filter bandwidth.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Same-frame GPU input/output comparison. Opt-in; no cost during ordinary playback. */
|
|
2
|
+
export class SpatialFilterProbe extends RenderExtension {
|
|
3
|
+
constructor(device: any);
|
|
4
|
+
enabled: boolean;
|
|
5
|
+
texture: any;
|
|
6
|
+
device: any;
|
|
7
|
+
record(frame: any): void;
|
|
8
|
+
read(): Promise<{
|
|
9
|
+
pixels: number;
|
|
10
|
+
changed_percent: number;
|
|
11
|
+
zero_variance_percent: number;
|
|
12
|
+
mean: number;
|
|
13
|
+
max: number;
|
|
14
|
+
}>;
|
|
15
|
+
}
|
|
16
|
+
import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
|
|
17
|
+
//# sourceMappingURL=SpatialFilterProbe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpatialFilterProbe.d.ts","sourceRoot":"","sources":["../../../../../src/shade/playground/ssr_reprojection/SpatialFilterProbe.js"],"names":[],"mappings":"AA2BA,wFAAwF;AACxF;IAKI,yBAAoD;IAHpD,iBAAgB;IAChB,aAAQ;IACR,YAAO;IAEP,yBAoBC;IACD;;;;;;OAWC;CACJ;gCAnE+B,6CAA6C"}
|