@woosh/meep-engine 3.17.1 → 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.
Files changed (265) hide show
  1. package/editor/actions/concrete/ComponentAddAction.d.ts +1 -0
  2. package/editor/actions/concrete/ComponentAddAction.d.ts.map +1 -1
  3. package/editor/actions/concrete/ComponentAddAction.js +4 -0
  4. package/editor/actions/concrete/ComponentRemoveAction.d.ts +1 -0
  5. package/editor/actions/concrete/ComponentRemoveAction.d.ts.map +1 -1
  6. package/editor/actions/concrete/ComponentRemoveAction.js +4 -0
  7. package/editor/actions/concrete/EntityCreateAction.d.ts +1 -0
  8. package/editor/actions/concrete/EntityCreateAction.d.ts.map +1 -1
  9. package/editor/actions/concrete/EntityCreateAction.js +7 -0
  10. package/editor/actions/concrete/EntityRemoveAction.d.ts +1 -0
  11. package/editor/actions/concrete/EntityRemoveAction.d.ts.map +1 -1
  12. package/editor/actions/concrete/EntityRemoveAction.js +4 -0
  13. package/editor/actions/concrete/PatchTerrainHeightAction.d.ts +10 -0
  14. package/editor/actions/concrete/PatchTerrainHeightAction.d.ts.map +1 -1
  15. package/editor/actions/concrete/PatchTerrainHeightAction.js +107 -85
  16. package/editor/actions/concrete/TransformModifyAction.d.ts +1 -0
  17. package/editor/actions/concrete/TransformModifyAction.d.ts.map +1 -1
  18. package/editor/actions/concrete/TransformModifyAction.js +4 -0
  19. package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
  20. package/editor/particles/effect/ParticleEffectDocument.js +10 -6
  21. package/editor/particles/effect/ParticleReferenceSimulation.d.ts.map +1 -1
  22. package/editor/particles/effect/ParticleReferenceSimulation.js +98 -4
  23. package/editor/particles/effect/make_starter_particle_effect.d.ts.map +1 -1
  24. package/editor/particles/effect/make_starter_particle_effect.js +0 -2
  25. package/editor/tools/engine/ToolEngine.d.ts +30 -0
  26. package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
  27. package/editor/tools/engine/ToolEngine.js +257 -188
  28. package/editor/view/ecs/EntityList.d.ts.map +1 -1
  29. package/editor/view/ecs/EntityList.js +6 -5
  30. package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
  31. package/editor/view/particles/effect/ParticleEmitterInspectorView.js +17 -9
  32. package/package.json +1 -1
  33. package/src/CODEC_LAYOUT_VERIFICATION.md +1 -2
  34. package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.d.ts +56 -0
  35. package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.d.ts.map +1 -0
  36. package/src/core/binary/meshopt/MESHOPT_VERTEX_BLOCK.js +73 -0
  37. package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.d.ts.map +1 -1
  38. package/src/core/binary/meshopt/meshopt_decode_vertex_buffer.js +18 -63
  39. package/src/core/binary/meshopt/meshopt_encode_byte_group.d.ts +53 -0
  40. package/src/core/binary/meshopt/meshopt_encode_byte_group.d.ts.map +1 -0
  41. package/src/core/binary/meshopt/meshopt_encode_byte_group.js +140 -0
  42. package/src/core/binary/meshopt/meshopt_encode_data_block.d.ts +32 -0
  43. package/src/core/binary/meshopt/meshopt_encode_data_block.d.ts.map +1 -0
  44. package/src/core/binary/meshopt/meshopt_encode_data_block.js +77 -0
  45. package/src/core/binary/meshopt/meshopt_encode_index_sequence.d.ts +46 -0
  46. package/src/core/binary/meshopt/meshopt_encode_index_sequence.d.ts.map +1 -0
  47. package/src/core/binary/meshopt/meshopt_encode_index_sequence.js +146 -0
  48. package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.d.ts +40 -0
  49. package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.d.ts.map +1 -0
  50. package/src/core/binary/meshopt/meshopt_encode_vertex_buffer.js +700 -0
  51. package/src/core/binary/meshopt/meshopt_write_uint_var.d.ts +24 -0
  52. package/src/core/binary/meshopt/meshopt_write_uint_var.d.ts.map +1 -0
  53. package/src/core/binary/meshopt/meshopt_write_uint_var.js +39 -0
  54. package/src/core/math/random/seededRandom_Mulberry32.d.ts.map +1 -1
  55. package/src/core/math/random/seededRandom_Mulberry32.js +4 -2
  56. package/src/core/process/undo/Action.d.ts +19 -0
  57. package/src/core/process/undo/Action.d.ts.map +1 -1
  58. package/src/core/process/undo/Action.js +17 -0
  59. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +78 -0
  60. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -0
  61. package/src/engine/graphics3/GPUParticleEmitterSystem.js +310 -0
  62. package/src/engine/graphics3/GraphicsEngine.d.ts +17 -0
  63. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  64. package/src/engine/graphics3/GraphicsEngine.js +30 -10
  65. package/src/engine/graphics3/particles/particle_gpu_records.d.ts.map +1 -1
  66. package/src/engine/graphics3/particles/particle_gpu_records.js +0 -2
  67. package/src/engine/interpolation/InterpolationSystem.d.ts +7 -0
  68. package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
  69. package/src/engine/interpolation/InterpolationSystem.js +10 -0
  70. package/src/engine/network/NetworkSession.d.ts.map +1 -1
  71. package/src/engine/network/NetworkSession.js +14 -34
  72. package/src/engine/network/time/RenderPlayout.d.ts +76 -0
  73. package/src/engine/network/time/RenderPlayout.d.ts.map +1 -0
  74. package/src/engine/network/time/RenderPlayout.js +163 -0
  75. package/src/engine/network/transport/adapters/WebSocketTransport.d.ts.map +1 -1
  76. package/src/engine/network/transport/adapters/WebSocketTransport.js +11 -4
  77. package/src/engine/physics/ecs/PhysicsSystem.d.ts +4 -12
  78. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  79. package/src/engine/physics/ecs/PhysicsSystem.js +39 -13
  80. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  81. package/src/engine/sound/simulation/core/VolumeField.d.ts.map +1 -1
  82. package/src/engine/sound/simulation/core/VolumeField.js +4 -1
  83. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +23 -2
  84. package/src/shade/playground/particle_system/README.md +139 -139
  85. package/src/shade/playground/particle_system/particle_prototype.d.ts.map +1 -1
  86. package/src/shade/playground/particle_system/particle_prototype.js +0 -2
  87. package/src/shade/playground/particle_system/particle_scene.d.ts +6 -9
  88. package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
  89. package/src/shade/playground/particle_system/particle_scene.js +6 -13
  90. package/src/shade/playground/ssr_reprojection/README.md +103 -0
  91. package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.d.ts +17 -0
  92. package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.d.ts.map +1 -0
  93. package/src/shade/playground/ssr_reprojection/SpatialFilterProbe.js +68 -0
  94. package/src/shade/playground/ssr_reprojection/index.html +79 -0
  95. package/src/shade/playground/ssr_reprojection/main.d.ts +2 -0
  96. package/src/shade/playground/ssr_reprojection/main.d.ts.map +1 -0
  97. package/src/shade/playground/ssr_reprojection/main.js +245 -0
  98. package/src/shade/playground/ssr_reprojection/scene.d.ts +18 -0
  99. package/src/shade/playground/ssr_reprojection/scene.d.ts.map +1 -0
  100. package/src/shade/playground/ssr_reprojection/scene.js +51 -0
  101. package/src/shade/renderer/Renderer.d.ts +2 -23
  102. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  103. package/src/shade/renderer/Renderer.js +185 -218
  104. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +5 -8
  105. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  106. package/src/shade/renderer/animation/GPUAnimationManager.js +12 -12
  107. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  108. package/src/shade/renderer/buffer/table/GPUTypedTable.js +6 -0
  109. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  110. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +7 -0
  111. package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.d.ts +4 -0
  112. package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.d.ts.map +1 -0
  113. package/src/shade/renderer/global_illumination/brick4/gpu/draw/chunk_brick4_sample_specular.js +51 -0
  114. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.d.ts.map +1 -1
  115. package/src/shade/renderer/global_illumination/brick4/gpu/draw/shader_brick4_specular.js +5 -35
  116. package/src/shade/renderer/particles/DESIGN.md +703 -514
  117. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +16 -4
  118. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  119. package/src/shade/renderer/particles/GPUParticleSystem.js +1189 -991
  120. package/src/shade/renderer/particles/ParticleConstants.d.ts +64 -0
  121. package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
  122. package/src/shade/renderer/particles/ParticleConstants.js +71 -0
  123. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts +57 -0
  124. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.d.ts.map +1 -0
  125. package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +181 -0
  126. package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.d.ts +7 -0
  127. package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.d.ts.map +1 -0
  128. package/src/shade/renderer/particles/bounds/chunk_particle_bounds_unknown.js +12 -0
  129. package/src/shade/renderer/particles/bounds/graph_particle_bounds.d.ts +33 -0
  130. package/src/shade/renderer/particles/bounds/graph_particle_bounds.d.ts.map +1 -0
  131. package/src/shade/renderer/particles/bounds/graph_particle_bounds.js +67 -0
  132. package/src/shade/renderer/particles/bounds/shader_particle_bounds.d.ts +40 -0
  133. package/src/shade/renderer/particles/bounds/shader_particle_bounds.d.ts.map +1 -0
  134. package/src/shade/renderer/particles/bounds/shader_particle_bounds.js +187 -0
  135. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +15 -2
  136. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts.map +1 -1
  137. package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +14 -8
  138. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts +58 -4
  139. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts.map +1 -1
  140. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.js +79 -4
  141. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts +23 -1
  142. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.d.ts.map +1 -1
  143. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js +35 -2
  144. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +4 -2
  145. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
  146. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +5 -8
  147. package/src/shade/renderer/particles/data/particle_emitter_record.d.ts.map +1 -1
  148. package/src/shade/renderer/particles/data/particle_emitter_record.js +0 -6
  149. package/src/shade/renderer/particles/graph_particles.d.ts +25 -3
  150. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  151. package/src/shade/renderer/particles/graph_particles.js +108 -31
  152. package/src/shade/renderer/particles/graph_particles_avboit.d.ts +6 -4
  153. package/src/shade/renderer/particles/graph_particles_avboit.d.ts.map +1 -1
  154. package/src/shade/renderer/particles/graph_particles_avboit.js +5 -5
  155. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +5 -0
  156. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
  157. package/src/shade/renderer/particles/runtime/EmitterRegistry.js +27 -6
  158. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts +4 -0
  159. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.d.ts.map +1 -1
  160. package/src/shade/renderer/particles/runtime/GPUParticleEmitterContext.js +6 -1
  161. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +35 -27
  162. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts.map +1 -1
  163. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +424 -425
  164. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts +20 -0
  165. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_warmup.d.ts.map +1 -0
  166. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts +4 -0
  167. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts.map +1 -1
  168. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +38 -2
  169. package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.d.ts +22 -0
  170. package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.d.ts.map +1 -0
  171. package/src/shade/renderer/particles/shaders/chunk_particle_spawn_budget.js +42 -0
  172. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +1 -1
  173. package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.d.ts.map +1 -1
  174. package/src/shade/renderer/particles/shaders/shader_particle_avboit_occupancy.js +53 -22
  175. package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.d.ts.map +1 -1
  176. package/src/shade/renderer/particles/shaders/shader_particle_emitter_tick.js +213 -216
  177. package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts +50 -0
  178. package/src/shade/renderer/particles/shaders/shader_particle_reclaim.d.ts.map +1 -0
  179. package/src/shade/renderer/particles/shaders/shader_particle_render.js +1 -1
  180. package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.d.ts +36 -0
  181. package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.d.ts.map +1 -0
  182. package/src/shade/renderer/particles/warmup/chunk_particle_warmup_step.js +102 -0
  183. package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts +102 -0
  184. package/src/shade/renderer/particles/warmup/graph_particle_warmup.d.ts.map +1 -0
  185. package/src/shade/renderer/particles/warmup/graph_particle_warmup.js +329 -0
  186. package/src/shade/renderer/particles/warmup/shader_particle_warmup.d.ts +112 -0
  187. package/src/shade/renderer/particles/warmup/shader_particle_warmup.d.ts.map +1 -0
  188. package/src/shade/renderer/particles/warmup/shader_particle_warmup.js +318 -0
  189. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts +35 -0
  190. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.d.ts.map +1 -0
  191. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance.js +108 -0
  192. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts +3 -0
  193. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.d.ts.map +1 -0
  194. package/src/shade/renderer/particles/warmup/shader_particle_warmup_advance_wide.js +70 -0
  195. package/src/shade/renderer/particles/warmup/warmup_schedule.d.ts +62 -0
  196. package/src/shade/renderer/particles/warmup/warmup_schedule.d.ts.map +1 -0
  197. package/src/shade/renderer/particles/warmup/warmup_schedule.js +55 -0
  198. package/src/shade/renderer/postprocess/PostProcess.d.ts.map +1 -1
  199. package/src/shade/renderer/postprocess/PostProcess.js +2 -1
  200. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +286 -0
  201. package/src/shade/renderer/postprocess/ssr/SSR.d.ts +57 -21
  202. package/src/shade/renderer/postprocess/ssr/SSR.d.ts.map +1 -1
  203. package/src/shade/renderer/postprocess/ssr/SSR.js +230 -129
  204. package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.d.ts +11 -0
  205. package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.d.ts.map +1 -0
  206. package/src/shade/renderer/postprocess/ssr/SSRReprojectionMode.js +6 -0
  207. package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.d.ts +4 -0
  208. package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.d.ts.map +1 -0
  209. package/src/shade/renderer/postprocess/ssr/chunk_ssr_metadata.js +32 -0
  210. package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.d.ts +4 -0
  211. package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.d.ts.map +1 -0
  212. package/src/shade/renderer/postprocess/ssr/chunk_ssr_spatial_variance.js +41 -0
  213. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts +3 -0
  214. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.d.ts.map +1 -0
  215. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_reprojection.js +30 -0
  216. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts +4 -0
  217. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.d.ts.map +1 -0
  218. package/src/shade/renderer/postprocess/ssr/reproject/chunk_ssr_sample_history.js +70 -0
  219. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts +21 -0
  220. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.d.ts.map +1 -0
  221. package/src/shade/renderer/postprocess/ssr/resolve/build_ssr_resolve_shader.js +363 -0
  222. package/src/shade/renderer/postprocess/ssr/resolve/chunk_get_neighbour_weight.js +1 -1
  223. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.d.ts +2 -0
  224. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.d.ts.map +1 -0
  225. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_Brick4.js +24 -0
  226. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts +1 -6
  227. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.d.ts.map +1 -1
  228. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_IBL.js +16 -332
  229. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts +1 -6
  230. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.d.ts.map +1 -1
  231. package/src/shade/renderer/postprocess/ssr/resolve/ssr_resolve_shader_LPV.js +25 -358
  232. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts +0 -5
  233. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.d.ts.map +1 -1
  234. package/src/shade/renderer/postprocess/ssr/ssr_reproject_shader.js +201 -276
  235. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts +1 -0
  236. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.d.ts.map +1 -1
  237. package/src/shade/renderer/postprocess/ssr/ssr_spatial_denoise_shader.js +167 -143
  238. package/src/shade/renderer/shader/graph/graph_create_buffer.d.ts +15 -0
  239. package/src/shade/renderer/shader/graph/graph_create_buffer.d.ts.map +1 -0
  240. package/src/shade/renderer/shader/graph/graph_create_buffer.js +20 -0
  241. package/src/shade/renderer/view/GPUViewContext.d.ts +3 -4
  242. package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
  243. package/src/shade/renderer/view/GPUViewContext.js +5 -5
  244. package/src/core/binary/meshopt/__meshopt_test_streams.d.ts +0 -24
  245. package/src/core/binary/meshopt/__meshopt_test_streams.d.ts.map +0 -1
  246. package/src/core/binary/meshopt/__meshopt_test_streams.js +0 -237
  247. package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts +0 -84
  248. package/src/core/geom/3d/atlas/atlas_test_fixtures.d.ts.map +0 -1
  249. package/src/core/geom/3d/atlas/atlas_test_fixtures.js +0 -266
  250. package/src/engine/asset/loaders/gltf_test_fixtures.d.ts +0 -97
  251. package/src/engine/asset/loaders/gltf_test_fixtures.d.ts.map +0 -1
  252. package/src/engine/asset/loaders/gltf_test_fixtures.js +0 -359
  253. package/src/engine/sound/simulation/core/OcclusionSolver.d.ts +0 -29
  254. package/src/engine/sound/simulation/core/OcclusionSolver.d.ts.map +0 -1
  255. package/src/shade/renderer/animation/skin_test_fixtures.d.ts +0 -86
  256. package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +0 -1
  257. package/src/shade/renderer/animation/skin_test_fixtures.js +0 -250
  258. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts +0 -16
  259. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts.map +0 -1
  260. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +0 -27
  261. package/src/shade/renderer/particles/particle_test_fixtures.d.ts +0 -151
  262. package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +0 -1
  263. package/src/shade/renderer/particles/particle_test_fixtures.js +0 -229
  264. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.js +0 -23
  265. 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
- | `__meshopt_test_streams.js` | test-only encoders, shared by the codec specs and the glTF fixtures |
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, the bounds spheres hold their effects, and
119
- `configure_particles` turns the feature on, selects 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 bounds spheres are read by the occupancy pass all the same.
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;EAgHpE;AAED;;;;GAIG;AACH;YAFsB,cAAc;YAAU,MAAM;aAAW,MAAM;EAapE;AAED;;;;;;;;;;;;;GAaG;AACH,yCANW,OAAO,+BAA+B,EAAE,KAAK;IAEa,IAAI,GAA9D,OAAO,gCAAgC,EAAE,MAAM;;UAEtC,eAAe;WAAS,eAAe;EAe1D;+BA3L8B,mDAAmD;gCAElD,qDAAqD"}
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
- * Every one carries a bounds sphere that holds the effect it emits. Under AVBOIT that sphere is
6
- * not just the culling test it used to be: it is what marks the depth slices the emitter may
7
- * occlude in, so an emitter whose particles outrun it occludes at slightly the wrong depth. The
8
- * spheres are sized to hold where the effect actually goes rather than to the last reachable
9
- * corner of its jitter: a sphere big enough for the worst case would mark most of the depth range
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 because the bounds spheres below are authored against these numbers
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":"AA0FA;;;;;;;;;;;;GAYG;AACH;UAFoB,eAAe;WAAS,eAAe;YAAU,eAAe;UAAQ,eAAe;WAAS,eAAe;WAAS,MAAM;EAmEjJ;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;AAnUD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,0FAA0F;AAC1F,sCAAuC;AAKvC;;;;;;;;;;;GAWG;AAEH;;;;;;;GAOG;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;;;;;WAxBY,MAAM;;;;UACN,MAAM;;;;kBACN,MAAM;;;;;cACN,MAAM;;;;;gBAEN,MAAM;;;;cAEN,MAAM,EAAE;;;;UACR,MAAM,EAAE;;gCAjDU,qDAAqD;uBAE9D,gCAAgC;sBACjC,+BAA+B;qBAFhC,8BAA8B"}
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 because the bounds spheres below are authored against these numbers
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
- * Every one carries a bounds sphere that holds the effect it emits. Under AVBOIT that sphere is
96
- * not just the culling test it used to be: it is what marks the depth slices the emitter may
97
- * occlude in, so an emitter whose particles outrun it occludes at slightly the wrong depth. The
98
- * spheres are sized to hold where the effect actually goes rather than to the last reachable
99
- * corner of its jitter: a sphere big enough for the worst case would mark most of the depth range
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"}