@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.
Files changed (263) 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/shade/renderer/animation/skin_test_fixtures.d.ts +0 -86
  254. package/src/shade/renderer/animation/skin_test_fixtures.d.ts.map +0 -1
  255. package/src/shade/renderer/animation/skin_test_fixtures.js +0 -250
  256. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts +0 -16
  257. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.d.ts.map +0 -1
  258. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_test_fixtures.js +0 -27
  259. package/src/shade/renderer/particles/particle_test_fixtures.d.ts +0 -151
  260. package/src/shade/renderer/particles/particle_test_fixtures.d.ts.map +0 -1
  261. package/src/shade/renderer/particles/particle_test_fixtures.js +0 -229
  262. package/src/shade/renderer/particles/shaders/chunk_particle_emitter_world_sphere.js +0 -23
  263. package/src/shade/renderer/postprocess/ssr/reproject/shader_ffx_denoiser_reflections_reproject.js +0 -457
@@ -0,0 +1,187 @@
1
+ import { atomic_u32, u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
2
+ import { WebGPUArray } from "../../shader/type/WebGPUArray.js";
3
+ import { AABB3_STRUCT } from "../../shader/chunk/geometry/aabb3/AABB3_STRUCT.js";
4
+ import { ComputeShader } from "../../shader/ComputeShader.js";
5
+ import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
6
+ import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
7
+ import {
8
+ PARTICLE_EMITTER_TICK_WORKGROUP_SIZE,
9
+ PARTICLE_RECORD_HEADER_EMITTER,
10
+ PARTICLE_RECORD_WORD_COUNT,
11
+ PARTICLE_SIMULATE_WORKGROUP_SIZE,
12
+ } from "../ParticleConstants.js";
13
+ import { PARTICLE_COUNTER } from "../data/PARTICLE_COUNTERS.js";
14
+ import { PARTICLE_EMITTER_DESCRIPTOR, PARTICLE_EMITTER_STATE_DESCRIPTOR } from "../data/PARTICLE_DATABASE_SPEC.js";
15
+ import { chunk_particle_header } from "../data/particle_header.js";
16
+ import { chunk_particle_bounds_of_record, chunk_particle_bounds_store } from "./chunk_particle_bounds.js";
17
+
18
+ /**
19
+ * The two passes that measure every emitter's bounding box, at the end of the frame that produced
20
+ * the particles.
21
+ *
22
+ * - {@link shader_particle_bounds_accumulate} — one thread per live particle: read where its quad
23
+ * is and merge it into its emitter's accumulator.
24
+ * - {@link shader_particle_bounds_publish} — one thread per emitter row: hand the accumulation to
25
+ * the state row the culling and occupancy passes read, and start the next frame's measurement.
26
+ *
27
+ * ## Why this is not a tail on the simulate pass
28
+ *
29
+ * It was, briefly. Two things are wrong with booking the box from inside emit and simulate. The
30
+ * measurement is not of one population: simulate sees last frame's survivors and emit sees this
31
+ * frame's newborns, so the union spans two frames and is looser than either. And the merge is six
32
+ * atomics in the pass whose register pressure decides the whole system's occupancy — the interpreter
33
+ * — paid to compute something that needs none of the VM, none of the scene, and five words of the
34
+ * record.
35
+ *
36
+ * Run over the alive list instead and the measurement is of exactly the set that is alive and about
37
+ * to be drawn, taken by a pass that reads a header, an emitter row and three attributes.
38
+ *
39
+ * ## Sizing
40
+ *
41
+ * The accumulate pass is dispatched indirectly from `sim_dispatch_args`, which the coherence reset
42
+ * wrote from the GPU's own alive count — the same args the render sort's two per-particle passes
43
+ * take, and the reason its workgroup size must stay {@link PARTICLE_SIMULATE_WORKGROUP_SIZE}, which
44
+ * is what those args were divided by. Its element bound is `counters[ALIVE]`, read in the shader.
45
+ * Nothing here is sized from the host.
46
+ *
47
+ * The publish pass walks the emitter table with the table's page iterator, one page group per
48
+ * workgroup, like the tick.
49
+ */
50
+
51
+ export { PARTICLE_SIMULATE_WORKGROUP_SIZE, PARTICLE_EMITTER_TICK_WORKGROUP_SIZE };
52
+
53
+ // --- accumulate -------------------------------------------------------------------------------
54
+
55
+ const accumulate_resources = new ShaderResourceSetDescriptor();
56
+
57
+ accumulate_resources.createGroup()
58
+ .addStorageBuffer("emitter_database", WebGPUArray.u32, false)
59
+ .addStorageBuffer("pool", WebGPUArray.u32, false)
60
+ .addStorageBuffer("alive", WebGPUArray.u32, false)
61
+ .addStorageBuffer("counters", WebGPUArray.from(atomic_u32), true);
62
+
63
+ /**
64
+ * One thread per live particle: merge its quad into its emitter's accumulator.
65
+ *
66
+ * The alive list holds real slots only — the padding sentinel belongs to the simulation-order list,
67
+ * and an orphan was killed by the simulate pass that built this list — so every entry is a particle
68
+ * whose emitter row is live and whose record has this frame's values in it.
69
+ *
70
+ * It decodes a whole emitter record to reach four of its fields, which the bucketing histogram
71
+ * deliberately does not (`chunk_particle_emitter_program_id`). The difference is what is being paid
72
+ * for: emitters are counted in tens, so the records are a working set that stays resident for the
73
+ * whole dispatch, while the pool reads are a gather over the free list and are what this pass costs.
74
+ * The billboard vertex stage reads a record the same way, per vertex.
75
+ *
76
+ * @type {ComputeShader}
77
+ */
78
+ export const shader_particle_bounds_accumulate = ComputeShader.from({
79
+ label: "Particle Bounds Accumulate",
80
+ resources: accumulate_resources,
81
+ body: CodeChunk.from(/* language=wgsl */ `
82
+ const PARTICLE_RECORD_WORDS : u32 = ${PARTICLE_RECORD_WORD_COUNT}u;
83
+ const PARTICLE_HEADER_EMITTER : u32 = ${PARTICLE_RECORD_HEADER_EMITTER}u;
84
+ const COUNTER_ALIVE : u32 = ${PARTICLE_COUNTER.ALIVE}u;
85
+
86
+ @compute @workgroup_size(${PARTICLE_SIMULATE_WORKGROUP_SIZE}, 1, 1)
87
+ fn main(@builtin(global_invocation_id) gid : vec3<u32>) {
88
+ let i = gid.x;
89
+
90
+ // This frame's alive count, as emit and simulate left it. The indirect dispatch was built from
91
+ // the same number, so the excess is at most a workgroup's tail.
92
+ if (i >= atomicLoad(&counters[COUNTER_ALIVE])) { return; }
93
+
94
+ let slot = alive[i];
95
+ let record_base = slot * PARTICLE_RECORD_WORDS;
96
+ let emitter_row = particle_header_emitter(pool[record_base + PARTICLE_HEADER_EMITTER]);
97
+
98
+ let em = particle_emitter_read(&emitter_database, emitter_row);
99
+ let sample = particle_bounds_of_record(em, &pool, record_base);
100
+
101
+ particle_bounds_contribute(emitter_row, sample.center, sample.radius);
102
+ }
103
+ `, [
104
+ chunk_particle_bounds_of_record,
105
+ chunk_particle_bounds_store,
106
+ chunk_particle_header,
107
+ ]),
108
+ });
109
+
110
+ // --- publish ----------------------------------------------------------------------------------
111
+
112
+ const PG = PARTICLE_EMITTER_DESCRIPTOR.page_iterator_symbol_prefix(PARTICLE_EMITTER_TICK_WORKGROUP_SIZE, true);
113
+ const STATE = PARTICLE_EMITTER_STATE_DESCRIPTOR;
114
+
115
+ // Generated once and held, because each call builds a fresh chunk declaring the same function: two
116
+ // calls for one field would be two declarations of it in the assembled shader.
117
+ const WRITE_BOUNDS_MIN = STATE.wgsl_gen_write_field_code("bounds_min");
118
+ const WRITE_BOUNDS_MAX = STATE.wgsl_gen_write_field_code("bounds_max");
119
+
120
+ const publish_resources = new ShaderResourceSetDescriptor();
121
+
122
+ publish_resources.createGroup()
123
+ .addStorageBuffer("emitter_database", WebGPUArray.u32, true)
124
+ .addStorageBuffer("counters", WebGPUArray.from(atomic_u32), true);
125
+
126
+ /**
127
+ * One thread per emitter table row: publish what the accumulate pass measured into
128
+ * `bounds_min` / `bounds_max` of the emitter's state row, and reset the accumulator.
129
+ *
130
+ * The state row rather than the accumulator directly, because the readers — the tick's frustum test
131
+ * next frame, the AVBOIT occupancy later in this one — should not each have to know the
132
+ * accumulator's layout or repeat the empty case below. It is also where the rest of an emitter's
133
+ * GPU-owned state lives, for the reason {@link ../data/PARTICLE_EMITTER_STATE.js} gives.
134
+ *
135
+ * ## The empty case
136
+ *
137
+ * No measured particles means unknown bounds, represented by an infinite box. The tick lets such
138
+ * an emitter spawn until a finite population can be measured; occupancy skips it because there is
139
+ * nothing to splat. This also lets an exhausted effect restart without requiring authored bounds
140
+ * or prewarm. Readers recognize the sentinel before doing geometry arithmetic on infinities.
141
+ *
142
+ * @type {ComputeShader}
143
+ */
144
+ export const shader_particle_bounds_publish = ComputeShader.from({
145
+ label: "Particle Bounds Publish",
146
+ resources: publish_resources,
147
+ body: CodeChunk.from(/* language=wgsl */ `
148
+ @compute @workgroup_size(${PARTICLE_EMITTER_TICK_WORKGROUP_SIZE}, 1, 1)
149
+ fn main(
150
+ @builtin(workgroup_id) wg_id : vec3<u32>,
151
+ @builtin(local_invocation_index) local_id : u32,
152
+ ) {
153
+ // page_address is uniform across the workgroup, so this branch is uniform.
154
+ if (!${PG}_setup(&emitter_database, wg_id.x, local_id)) {
155
+ return;
156
+ }
157
+
158
+ let slot_in_page = ${PG}_slot_in_page(local_id);
159
+
160
+ if (slot_in_page >= ${PG}_ELEMENTS_PER_PAGE || !${PG}_is_occupied(slot_in_page)) {
161
+ return;
162
+ }
163
+
164
+ let row = ${PG}_slot_to_index(slot_in_page);
165
+
166
+ var box = particle_bounds_read(row);
167
+
168
+ if (particle_bounds_is_empty(box)) {
169
+ // The empty accumulator already holds [+inf, -inf]. Swap the loaded endpoints to
170
+ // publish unknown bounds without constructing forbidden infinite constant expressions.
171
+ box = ${AABB3_STRUCT.make({ min: "box.max", max: "box.min" })};
172
+ }
173
+
174
+ particle_bounds_reset(row);
175
+
176
+ // Two field writes rather than a whole-row read-modify-write: the tick wrote this row earlier in
177
+ // the frame, and there is no reason for this pass to carry the rest of it through registers.
178
+ ${STATE.marshalling_method_write_field("bounds_min")}(&emitter_database, row, box.min);
179
+ ${STATE.marshalling_method_write_field("bounds_max")}(&emitter_database, row, box.max);
180
+ }
181
+ `, [
182
+ PARTICLE_EMITTER_DESCRIPTOR.chunk_iterate(PARTICLE_EMITTER_TICK_WORKGROUP_SIZE, true),
183
+ WRITE_BOUNDS_MIN,
184
+ WRITE_BOUNDS_MAX,
185
+ chunk_particle_bounds_store,
186
+ ]),
187
+ });
@@ -1,3 +1,6 @@
1
+ /**
2
+ * @typedef {{keys: number, histogram: number, cursor: number, bucket_counts: number, dispatch_args: number}} ParticleBucketScratch
3
+ */
1
4
  /**
2
5
  * Frame-graph wiring for the simulation-order bucketing: turns the frame's freshly compacted alive
3
6
  * list into the program-coherent, workgroup-padded list NEXT frame's simulate consumes.
@@ -36,9 +39,10 @@
36
39
  * GPUBuffer twice makes two resource entries the graph cannot see are the same memory, so the read
37
40
  * and the write end up on separate dependency chains
38
41
  * @param {number} params.bucket_count number of distinct compiled programs
39
- * @returns {{counters: number, sim_list: number, dispatch_args: number}} the threaded handles downstream passes must read
42
+ * @param {ParticleBucketScratch} [params.scratch] latest handles when bucketing again in this graph
43
+ * @returns {{counters: number, sim_list: number, dispatch_args: number, scratch: ParticleBucketScratch}} the threaded handles downstream passes must read
40
44
  */
41
- export function graph_particle_bucket({ graph, gpu, emitter_database, pool, alive, counters, sim_list, bucket_count }: {
45
+ export function graph_particle_bucket({ graph, gpu, emitter_database, pool, alive, counters, sim_list, bucket_count, scratch }: {
42
46
  graph: import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph;
43
47
  gpu: {
44
48
  sim_list: GPUBuffer;
@@ -54,9 +58,18 @@ export function graph_particle_bucket({ graph, gpu, emitter_database, pool, aliv
54
58
  counters: number;
55
59
  sim_list: number;
56
60
  bucket_count: number;
61
+ scratch?: ParticleBucketScratch;
57
62
  }): {
58
63
  counters: number;
59
64
  sim_list: number;
60
65
  dispatch_args: number;
66
+ scratch: ParticleBucketScratch;
67
+ };
68
+ export type ParticleBucketScratch = {
69
+ keys: number;
70
+ histogram: number;
71
+ cursor: number;
72
+ bucket_counts: number;
73
+ dispatch_args: number;
61
74
  };
62
75
  //# sourceMappingURL=graph_particle_bucket.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"graph_particle_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/coherence/graph_particle_bucket.js"],"names":[],"mappings":"AAkBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH;IAnBqG,KAAK,EAA/F,OAAO,8DAA8D,EAAE,UAAU;IAClE,GAAG;QACI,QAAQ;QACR,QAAQ;QACR,aAAa;QACb,iBAAiB;QACjB,UAAU;QACV,iBAAiB;;IACxB,gBAAgB,EAA/B,MAAM;IACS,IAAI,EAAnB,MAAM;IACS,KAAK,EAApB,MAAM;IACS,QAAQ,EAAvB,MAAM;IACS,QAAQ,EAAvB,MAAM;IAIS,YAAY,EAA3B,MAAM;;cACO,MAAM;cAAY,MAAM;mBAAiB,MAAM;EAwGtE"}
1
+ {"version":3,"file":"graph_particle_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/coherence/graph_particle_bucket.js"],"names":[],"mappings":"AAkBA;;GAEG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH;IApBqG,KAAK,EAA/F,OAAO,8DAA8D,EAAE,UAAU;IAClE,GAAG;QACI,QAAQ;QACR,QAAQ;QACR,aAAa;QACb,iBAAiB;QACjB,UAAU;QACV,iBAAiB;;IACxB,gBAAgB,EAA/B,MAAM;IACS,IAAI,EAAnB,MAAM;IACS,KAAK,EAApB,MAAM;IACS,QAAQ,EAAvB,MAAM;IACS,QAAQ,EAAvB,MAAM;IAIS,YAAY,EAA3B,MAAM;IACyB,OAAO,GAAtC,qBAAqB;;cACR,MAAM;cAAY,MAAM;mBAAiB,MAAM;aAAW,qBAAqB;EAyGtG;oCAnJY;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAC"}
@@ -16,6 +16,10 @@ import {
16
16
  shader_particle_bucket_scatter,
17
17
  } from "./shader_particle_bucket.js";
18
18
 
19
+ /**
20
+ * @typedef {{keys: number, histogram: number, cursor: number, bucket_counts: number, dispatch_args: number}} ParticleBucketScratch
21
+ */
22
+
19
23
  /**
20
24
  * Frame-graph wiring for the simulation-order bucketing: turns the frame's freshly compacted alive
21
25
  * list into the program-coherent, workgroup-padded list NEXT frame's simulate consumes.
@@ -54,9 +58,10 @@ import {
54
58
  * GPUBuffer twice makes two resource entries the graph cannot see are the same memory, so the read
55
59
  * and the write end up on separate dependency chains
56
60
  * @param {number} params.bucket_count number of distinct compiled programs
57
- * @returns {{counters: number, sim_list: number, dispatch_args: number}} the threaded handles downstream passes must read
61
+ * @param {ParticleBucketScratch} [params.scratch] latest handles when bucketing again in this graph
62
+ * @returns {{counters: number, sim_list: number, dispatch_args: number, scratch: ParticleBucketScratch}} the threaded handles downstream passes must read
58
63
  */
59
- export function graph_particle_bucket({ graph, gpu, emitter_database, pool, alive, counters, sim_list, bucket_count }) {
64
+ export function graph_particle_bucket({ graph, gpu, emitter_database, pool, alive, counters, sim_list, bucket_count, scratch }) {
60
65
  assert.defined(graph, "graph");
61
66
  assert.isPositiveInteger(bucket_count, "bucket_count");
62
67
  assert.isNonNegativeInteger(sim_list, "sim_list");
@@ -74,11 +79,12 @@ export function graph_particle_bucket({ graph, gpu, emitter_database, pool, aliv
74
79
  // between the two passes unexpressed — the graph's own validator rejects it, because both
75
80
  // versions name one piece of memory and the stale-looking read silently returns the new
76
81
  // contents.
77
- let keys = graph_import_buffer(graph, gpu.sim_keys, "particles/sim_keys");
78
- let histogram = graph_import_buffer(graph, gpu.sim_histogram, "particles/sim_histogram");
79
- let cursor = graph_import_buffer(graph, gpu.sim_cursor, "particles/sim_cursor");
80
- let bucket_counts = graph_import_buffer(graph, gpu.sim_bucket_counts, "particles/sim_bucket_counts");
81
- let dispatch_args = graph_import_buffer(graph, gpu.sim_dispatch_args, "particles/sim_dispatch_args");
82
+ // An edit frame buckets twice. Reuse the latest handles so both runs share one dependency chain.
83
+ let keys = scratch?.keys ?? graph_import_buffer(graph, gpu.sim_keys, "particles/sim_keys");
84
+ let histogram = scratch?.histogram ?? graph_import_buffer(graph, gpu.sim_histogram, "particles/sim_histogram");
85
+ let cursor = scratch?.cursor ?? graph_import_buffer(graph, gpu.sim_cursor, "particles/sim_cursor");
86
+ let bucket_counts = scratch?.bucket_counts ?? graph_import_buffer(graph, gpu.sim_bucket_counts, "particles/sim_bucket_counts");
87
+ let dispatch_args = scratch?.dispatch_args ?? graph_import_buffer(graph, gpu.sim_dispatch_args, "particles/sim_dispatch_args");
82
88
 
83
89
  // 0. Clear the bins and cursors, write the scan's {count} header, and size the two per-particle
84
90
  // passes below from the GPU's own alive count.
@@ -157,7 +163,7 @@ export function graph_particle_bucket({ graph, gpu, emitter_database, pool, aliv
157
163
 
158
164
  // `dispatch_args` goes back too: it holds one workgroup per live particle, written from the
159
165
  // GPU's own alive count, which is exactly the shape the depth sort's per-particle passes need.
160
- return { counters, sim_list, dispatch_args };
166
+ return { counters, sim_list, dispatch_args, scratch: { keys, histogram: scanned, cursor, bucket_counts, dispatch_args } };
161
167
  }
162
168
 
163
169
  /**
@@ -1,7 +1,30 @@
1
1
  /**
2
- * Indices into the particle counters buffer (an `array<atomic<u32>>`). The simulate/emit passes use
3
- * atomics on these slots to append to the alive/dead lists; a finalize pass turns the counts into
4
- * indirect dispatch/draw arguments.
2
+ * How many words the counters buffer needs to hold the counters plus one accumulator per row of an
3
+ * emitter table of `emitter_capacity` elements.
4
+ *
5
+ * @param {number} emitter_capacity element capacity of the emitter table
6
+ * @returns {number}
7
+ */
8
+ export function particle_counters_word_count(emitter_capacity: number): number;
9
+ /**
10
+ * `row_count` accumulators, each holding the empty interval — what the publish pass leaves behind,
11
+ * and what a row must hold before anything has measured it.
12
+ *
13
+ * A zero-filled accumulator is NOT that: it reads back as a valid degenerate box at the world
14
+ * origin, which would union the origin into the first box measured for the row. WebGPU zero-fills a
15
+ * new buffer, so the host writes this over a newly grown region rather than relying on the fill.
16
+ *
17
+ * @param {number} row_count
18
+ * @returns {Uint32Array}
19
+ */
20
+ export function particle_bounds_empty_words(row_count: number): Uint32Array;
21
+ /**
22
+ * Indices into the head of the particle counters buffer (an `array<atomic<u32>>`). The simulate/emit
23
+ * passes use atomics on these slots to append to the alive/dead lists; a finalize pass turns the
24
+ * counts into indirect dispatch/draw arguments.
25
+ *
26
+ * The buffer does not end here: {@link PARTICLE_BOUNDS_BASE} onward is the per-emitter bounds
27
+ * accumulator, described below.
5
28
  */
6
29
  export const PARTICLE_COUNTER: Readonly<{
7
30
  /** length of the alive list produced this frame */
@@ -47,6 +70,37 @@ export const PARTICLE_COUNTER: Readonly<{
47
70
  */
48
71
  SIM_PADDED: 5;
49
72
  }>;
50
- /** Number of `u32` atomic slots in the counters buffer. */
73
+ /** Number of `u32` atomic slots the counters themselves occupy — the head of the buffer. */
51
74
  export const PARTICLE_COUNTER_COUNT: 8;
75
+ /**
76
+ * First word of the per-emitter bounds accumulator, which occupies the rest of the counters buffer:
77
+ * {@link PARTICLE_BOUNDS_WORDS} words per emitter table row, indexed by row.
78
+ *
79
+ * ## Why it lives here rather than in a buffer of its own
80
+ *
81
+ * The accumulator is merged into with atomics — one contribution per live particle — which means a
82
+ * binding declared `array<atomic<u32>>`. The emitter database cannot be it: the passes that read a
83
+ * record read it out of an `array<u32>` binding, and one buffer under two access types is aliasing.
84
+ *
85
+ * The counters buffer is already exactly that: bound in both bounds passes, already atomic, already
86
+ * read-write, and already this system's cross-pass GPU-owned scratch. Giving the accumulator a
87
+ * buffer of its own would add a binding and a growth path to say the same thing. So it is a section
88
+ * of this one: the counters keep their slots, the reset pass keeps writing the same fixed indices,
89
+ * and the section is addressed off this base.
90
+ *
91
+ * @type {number}
92
+ */
93
+ export const PARTICLE_BOUNDS_BASE: number;
94
+ /**
95
+ * Words one emitter's bounds accumulator takes: `min.xyz` then `max.xyz`, each an f32 bit pattern
96
+ * held in a `u32` slot (see `../bounds/chunk_particle_bounds.js` for why the storage is plain f32
97
+ * rather than an order-preserving encoding).
98
+ *
99
+ * @type {number}
100
+ */
101
+ export const PARTICLE_BOUNDS_WORDS: number;
102
+ /** `bitcast<u32>(f32(+inf))` — the lower corner of the empty interval. */
103
+ export const PARTICLE_BOUNDS_PLUS_INF: 2139095040;
104
+ /** `bitcast<u32>(f32(-inf))` — the upper corner of the empty interval. */
105
+ export const PARTICLE_BOUNDS_MINUS_INF: 4286578688;
52
106
  //# sourceMappingURL=PARTICLE_COUNTERS.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PARTICLE_COUNTERS.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/PARTICLE_COUNTERS.js"],"names":[],"mappings":"AAAA;;;;GAIG;AACH;IACI,mDAAmD;;IAEnD,qCAAqC;;IAErC;;;;;OAKG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;;;;;OAWG;;IAEH;;;;;;;;OAQG;;GAEJ;AAEH,2DAA2D;AAC3D,uCAAwC"}
1
+ {"version":3,"file":"PARTICLE_COUNTERS.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/PARTICLE_COUNTERS.js"],"names":[],"mappings":"AAqFA;;;;;;GAMG;AACH,+DAHW,MAAM,GACJ,MAAM,CAIlB;AAQD;;;;;;;;;;GAUG;AACH,uDAHW,MAAM,GACJ,WAAW,CAevB;AA9HD;;;;;;;GAOG;AACH;IACI,mDAAmD;;IAEnD,qCAAqC;;IAErC;;;;;OAKG;;IAEH;;;;;;OAMG;;IAEH;;;;;;;;;;;OAWG;;IAEH;;;;;;;;OAQG;;GAEJ;AAEH,4FAA4F;AAC5F,uCAAwC;AAExC;;;;;;;;;;;;;;;;;GAiBG;AACH,mCAFU,MAAM,CAE2C;AAE3D;;;;;;GAMG;AACH,oCAFU,MAAM,CAEuB;AAavC,0EAA0E;AAC1E,kDAAmD;AAEnD,0EAA0E;AAC1E,mDAAoD"}
@@ -1,7 +1,10 @@
1
1
  /**
2
- * Indices into the particle counters buffer (an `array<atomic<u32>>`). The simulate/emit passes use
3
- * atomics on these slots to append to the alive/dead lists; a finalize pass turns the counts into
4
- * indirect dispatch/draw arguments.
2
+ * Indices into the head of the particle counters buffer (an `array<atomic<u32>>`). The simulate/emit
3
+ * passes use atomics on these slots to append to the alive/dead lists; a finalize pass turns the
4
+ * counts into indirect dispatch/draw arguments.
5
+ *
6
+ * The buffer does not end here: {@link PARTICLE_BOUNDS_BASE} onward is the per-emitter bounds
7
+ * accumulator, described below.
5
8
  */
6
9
  export const PARTICLE_COUNTER = Object.freeze({
7
10
  /** length of the alive list produced this frame */
@@ -48,5 +51,77 @@ export const PARTICLE_COUNTER = Object.freeze({
48
51
  SIM_PADDED: 5,
49
52
  });
50
53
 
51
- /** Number of `u32` atomic slots in the counters buffer. */
54
+ /** Number of `u32` atomic slots the counters themselves occupy — the head of the buffer. */
52
55
  export const PARTICLE_COUNTER_COUNT = 8;
56
+
57
+ /**
58
+ * First word of the per-emitter bounds accumulator, which occupies the rest of the counters buffer:
59
+ * {@link PARTICLE_BOUNDS_WORDS} words per emitter table row, indexed by row.
60
+ *
61
+ * ## Why it lives here rather than in a buffer of its own
62
+ *
63
+ * The accumulator is merged into with atomics — one contribution per live particle — which means a
64
+ * binding declared `array<atomic<u32>>`. The emitter database cannot be it: the passes that read a
65
+ * record read it out of an `array<u32>` binding, and one buffer under two access types is aliasing.
66
+ *
67
+ * The counters buffer is already exactly that: bound in both bounds passes, already atomic, already
68
+ * read-write, and already this system's cross-pass GPU-owned scratch. Giving the accumulator a
69
+ * buffer of its own would add a binding and a growth path to say the same thing. So it is a section
70
+ * of this one: the counters keep their slots, the reset pass keeps writing the same fixed indices,
71
+ * and the section is addressed off this base.
72
+ *
73
+ * @type {number}
74
+ */
75
+ export const PARTICLE_BOUNDS_BASE = PARTICLE_COUNTER_COUNT;
76
+
77
+ /**
78
+ * Words one emitter's bounds accumulator takes: `min.xyz` then `max.xyz`, each an f32 bit pattern
79
+ * held in a `u32` slot (see `../bounds/chunk_particle_bounds.js` for why the storage is plain f32
80
+ * rather than an order-preserving encoding).
81
+ *
82
+ * @type {number}
83
+ */
84
+ export const PARTICLE_BOUNDS_WORDS = 6;
85
+
86
+ /**
87
+ * How many words the counters buffer needs to hold the counters plus one accumulator per row of an
88
+ * emitter table of `emitter_capacity` elements.
89
+ *
90
+ * @param {number} emitter_capacity element capacity of the emitter table
91
+ * @returns {number}
92
+ */
93
+ export function particle_counters_word_count(emitter_capacity) {
94
+ return PARTICLE_BOUNDS_BASE + emitter_capacity * PARTICLE_BOUNDS_WORDS;
95
+ }
96
+
97
+ /** `bitcast<u32>(f32(+inf))` — the lower corner of the empty interval. */
98
+ export const PARTICLE_BOUNDS_PLUS_INF = 0x7F800000;
99
+
100
+ /** `bitcast<u32>(f32(-inf))` — the upper corner of the empty interval. */
101
+ export const PARTICLE_BOUNDS_MINUS_INF = 0xFF800000;
102
+
103
+ /**
104
+ * `row_count` accumulators, each holding the empty interval — what the publish pass leaves behind,
105
+ * and what a row must hold before anything has measured it.
106
+ *
107
+ * A zero-filled accumulator is NOT that: it reads back as a valid degenerate box at the world
108
+ * origin, which would union the origin into the first box measured for the row. WebGPU zero-fills a
109
+ * new buffer, so the host writes this over a newly grown region rather than relying on the fill.
110
+ *
111
+ * @param {number} row_count
112
+ * @returns {Uint32Array}
113
+ */
114
+ export function particle_bounds_empty_words(row_count) {
115
+ const words = new Uint32Array(row_count * PARTICLE_BOUNDS_WORDS);
116
+
117
+ for (let word = 0; word < words.length; word += PARTICLE_BOUNDS_WORDS) {
118
+ words[word] = PARTICLE_BOUNDS_PLUS_INF;
119
+ words[word + 1] = PARTICLE_BOUNDS_PLUS_INF;
120
+ words[word + 2] = PARTICLE_BOUNDS_PLUS_INF;
121
+ words[word + 3] = PARTICLE_BOUNDS_MINUS_INF;
122
+ words[word + 4] = PARTICLE_BOUNDS_MINUS_INF;
123
+ words[word + 5] = PARTICLE_BOUNDS_MINUS_INF;
124
+ }
125
+
126
+ return words;
127
+ }
@@ -21,12 +21,32 @@
21
21
  * against the record's and starts the row over on a mismatch. A freshly allocated page and a reused
22
22
  * row both fall out of that one rule.
23
23
  *
24
+ * ## The bounds are here for the same reason everything else here is
25
+ *
26
+ * An emitter has no bounds an author could state. Its particles are simulated in world space by
27
+ * arbitrary bytecode and go where that bytecode sends them, at whatever size it gives them; the only
28
+ * honest answer written down in advance is "an infinite sphere", which culls nothing. So the box is
29
+ * measured instead of declared: at the end of each frame a pass over the alive list reduces the live
30
+ * particles' world-space quads into a per-row accumulator, and a second pass publishes that into
31
+ * {@link bounds_min} / {@link bounds_max} here. Both are `../bounds/`.
32
+ *
33
+ * Measured state is GPU-owned state, and this is the table GPU-owned state lives in. On the emitter
34
+ * record it would be overwritten by the next restage — the same hazard the accumulator has, one
35
+ * paragraph up.
36
+ *
24
37
  * @type {WebGPUStruct}
25
38
  */
26
39
  export const PARTICLE_EMITTER_STATE_STRUCT: WebGPUStruct;
27
40
  /**
28
41
  * A state row as the CPU stages it when an emitter claims a row: nothing yet. Generation zero is
29
- * never a live emitter's, so the first tick starts the row over whatever it held before.
42
+ * never a live emitter's, so the first tick starts the row over whatever it held before, and the
43
+ * first publish replaces the bounds with a measurement.
44
+ *
45
+ * The infinite box means unknown until the bounds passes have a live population to measure.
46
+ *
47
+ * Every field is present even though the first tick overwrites all of them, because the serializer
48
+ * skips absent fields and table pages are recycled buffers: a skipped field would keep the previous
49
+ * tenant's bytes rather than being zeroed.
30
50
  */
31
51
  export const PARTICLE_EMITTER_STATE_EMPTY: Readonly<{
32
52
  generation: 0;
@@ -34,6 +54,8 @@ export const PARTICLE_EMITTER_STATE_EMPTY: Readonly<{
34
54
  age: 0;
35
55
  sleep: 0;
36
56
  pending: 0;
57
+ bounds_min: readonly number[];
58
+ bounds_max: readonly number[];
37
59
  }>;
38
60
  import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
39
61
  //# sourceMappingURL=PARTICLE_EMITTER_STATE.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PARTICLE_EMITTER_STATE.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,4CAFU,YAAY,CAaY;AAElC;;;GAGG;AACH;;;;;;GAMG;6BAlD0B,mCAAmC"}
1
+ {"version":3,"file":"PARTICLE_EMITTER_STATE.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/PARTICLE_EMITTER_STATE.js"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,4CAFU,YAAY,CAwBY;AAElC;;;;;;;;;;GAUG;AACH;;;;;;;;GAQG;6BAnF0B,mCAAmC"}
@@ -1,4 +1,4 @@
1
- import { f32, u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
1
+ import { f32, u32, vec3f } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
2
2
  import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
3
3
 
4
4
  /**
@@ -24,6 +24,19 @@ import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
24
24
  * against the record's and starts the row over on a mismatch. A freshly allocated page and a reused
25
25
  * row both fall out of that one rule.
26
26
  *
27
+ * ## The bounds are here for the same reason everything else here is
28
+ *
29
+ * An emitter has no bounds an author could state. Its particles are simulated in world space by
30
+ * arbitrary bytecode and go where that bytecode sends them, at whatever size it gives them; the only
31
+ * honest answer written down in advance is "an infinite sphere", which culls nothing. So the box is
32
+ * measured instead of declared: at the end of each frame a pass over the alive list reduces the live
33
+ * particles' world-space quads into a per-row accumulator, and a second pass publishes that into
34
+ * {@link bounds_min} / {@link bounds_max} here. Both are `../bounds/`.
35
+ *
36
+ * Measured state is GPU-owned state, and this is the table GPU-owned state lives in. On the emitter
37
+ * record it would be overwritten by the next restage — the same hazard the accumulator has, one
38
+ * paragraph up.
39
+ *
27
40
  * @type {WebGPUStruct}
28
41
  */
29
42
  export const PARTICLE_EMITTER_STATE_STRUCT = WebGPUStruct.from({
@@ -37,11 +50,29 @@ export const PARTICLE_EMITTER_STATE_STRUCT = WebGPUStruct.from({
37
50
  sleep: f32,
38
51
  /** particles requested by the spawn-command pass since the last tick, consumed by the tick */
39
52
  pending: u32,
53
+
54
+ /**
55
+ * World-space lower corner of the box holding this emitter's live particles, as the bounds
56
+ * passes left it at the end of the previous frame.
57
+ *
58
+ * Infinite while no live particles have supplied a measurement. The tick treats this as
59
+ * visible; AVBOIT occupancy skips it. Readers test the sentinel before arithmetic on infinity.
60
+ */
61
+ bounds_min: vec3f,
62
+ /** world-space upper corner; see {@link bounds_min} */
63
+ bounds_max: vec3f,
40
64
  }, "ParticleEmitterState").pack();
41
65
 
42
66
  /**
43
67
  * A state row as the CPU stages it when an emitter claims a row: nothing yet. Generation zero is
44
- * never a live emitter's, so the first tick starts the row over whatever it held before.
68
+ * never a live emitter's, so the first tick starts the row over whatever it held before, and the
69
+ * first publish replaces the bounds with a measurement.
70
+ *
71
+ * The infinite box means unknown until the bounds passes have a live population to measure.
72
+ *
73
+ * Every field is present even though the first tick overwrites all of them, because the serializer
74
+ * skips absent fields and table pages are recycled buffers: a skipped field would keep the previous
75
+ * tenant's bytes rather than being zeroed.
45
76
  */
46
77
  export const PARTICLE_EMITTER_STATE_EMPTY = Object.freeze({
47
78
  generation: 0,
@@ -49,4 +80,6 @@ export const PARTICLE_EMITTER_STATE_EMPTY = Object.freeze({
49
80
  age: 0,
50
81
  sleep: 0,
51
82
  pending: 0,
83
+ bounds_min: Object.freeze([-Infinity, -Infinity, -Infinity]),
84
+ bounds_max: Object.freeze([Infinity, Infinity, Infinity]),
52
85
  });
@@ -25,7 +25,7 @@ export function emitter_flags_projection(flags: number): number;
25
25
  * One emitter's GPU record: which compiled program it runs and where that program's code and
26
26
  * constants sit in the {@link ../runtime/ProgramArena.js} buffers, plus the instance parameters the
27
27
  * tick / emit / simulate / sort / render passes read — the scene node it hangs off, spawn rate,
28
- * feature flags, bounds sphere, render-attribute bindings, atlas region and flipbook grid.
28
+ * feature flags, render-attribute bindings, atlas region and flipbook grid.
29
29
  *
30
30
  * There is no transform here. An emitter is a scene node, and its world matrix is the `global` of
31
31
  * its row in the scene database's `transforms` table, composed on the GPU by the hierarchy pass
@@ -33,7 +33,9 @@ export function emitter_flags_projection(flags: number): number;
33
33
  *
34
34
  * Nor is there any per-frame state: what the GPU integrates for an emitter (spawn accumulator, age,
35
35
  * sleep) lives in {@link ./PARTICLE_EMITTER_STATE.js}, keyed by the same row, for the reason given
36
- * there.
36
+ * there. The bounding box the culling and occupancy passes test is there too, and for a second
37
+ * reason: it is measured from the particles rather than declared by an author, so it is the GPU's
38
+ * value and not the CPU's.
37
39
  *
38
40
  * It lives in a `GPUDatabase` table (see {@link ./PARTICLE_DATABASE_SPEC.js}), so the layout below
39
41
  * is the *only* description of it that exists: the JS writer is
@@ -1 +1 @@
1
- {"version":3,"file":"PARTICLE_EMITTER_STRUCT.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js"],"names":[],"mappings":"AAmIA;;;;;;;;;;GAUG;AACH;IAR6B,QAAQ,GAA1B,OAAO;IACW,UAAU,GAA5B,OAAO;IACW,IAAI,GAAtB,OAAO;IACW,IAAI,GAAtB,OAAO;IACU,KAAK,GAAtB,MAAM;IACW,UAAU,GAA3B,MAAM;IACJ,MAAM,CAWlB;AAED,6DAA6D;AAC7D,2CADY,MAAM,GAAkB,MAAM,CAGzC;AAED,kEAAkE;AAClE,gDADY,MAAM,GAAkB,MAAM,CAGzC;AA9JD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,sCAFU,YAAY,CAoEO;AAE7B,uDAAuD;AACvD,kDAAmD;AAEnD;;GAEG;AACH;;;;;GAKG;AAEH,kGAAkG;AAClG;;;;GAIG;AAKH,kEAAkE;AAClE;;;;;;GAMG;AAqCH,uEAAuE;AACvE;;;;;GAEG;6BArK0B,mCAAmC"}
1
+ {"version":3,"file":"PARTICLE_EMITTER_STRUCT.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js"],"names":[],"mappings":"AAgIA;;;;;;;;;;GAUG;AACH;IAR6B,QAAQ,GAA1B,OAAO;IACW,UAAU,GAA5B,OAAO;IACW,IAAI,GAAtB,OAAO;IACW,IAAI,GAAtB,OAAO;IACU,KAAK,GAAtB,MAAM;IACW,UAAU,GAA3B,MAAM;IACJ,MAAM,CAWlB;AAED,6DAA6D;AAC7D,2CADY,MAAM,GAAkB,MAAM,CAGzC;AAED,kEAAkE;AAClE,gDADY,MAAM,GAAkB,MAAM,CAGzC;AA3JD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,sCAFU,YAAY,CA+DO;AAE7B,uDAAuD;AACvD,kDAAmD;AAEnD;;GAEG;AACH;;;;;GAKG;AAEH,kGAAkG;AAClG;;;;GAIG;AAKH,kEAAkE;AAClE;;;;;;GAMG;AAqCH,uEAAuE;AACvE;;;;;GAEG;6BAlK0B,mCAAmC"}
@@ -1,11 +1,11 @@
1
- import { f32, u32, vec2f, vec3f, vec4f } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
1
+ import { f32, u32, vec2f, vec4f } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
2
2
  import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
3
3
 
4
4
  /**
5
5
  * One emitter's GPU record: which compiled program it runs and where that program's code and
6
6
  * constants sit in the {@link ../runtime/ProgramArena.js} buffers, plus the instance parameters the
7
7
  * tick / emit / simulate / sort / render passes read — the scene node it hangs off, spawn rate,
8
- * feature flags, bounds sphere, render-attribute bindings, atlas region and flipbook grid.
8
+ * feature flags, render-attribute bindings, atlas region and flipbook grid.
9
9
  *
10
10
  * There is no transform here. An emitter is a scene node, and its world matrix is the `global` of
11
11
  * its row in the scene database's `transforms` table, composed on the GPU by the hierarchy pass
@@ -13,7 +13,9 @@ import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
13
13
  *
14
14
  * Nor is there any per-frame state: what the GPU integrates for an emitter (spawn accumulator, age,
15
15
  * sleep) lives in {@link ./PARTICLE_EMITTER_STATE.js}, keyed by the same row, for the reason given
16
- * there.
16
+ * there. The bounding box the culling and occupancy passes test is there too, and for a second
17
+ * reason: it is measured from the particles rather than declared by an author, so it is the GPU's
18
+ * value and not the CPU's.
17
19
  *
18
20
  * It lives in a `GPUDatabase` table (see {@link ./PARTICLE_DATABASE_SPEC.js}), so the layout below
19
21
  * is the *only* description of it that exists: the JS writer is
@@ -58,11 +60,6 @@ export const PARTICLE_EMITTER_STRUCT = WebGPUStruct.from({
58
60
  */
59
61
  generation: u32,
60
62
 
61
- /** local-space bounding sphere centre */
62
- bounds_center: vec3f,
63
- /** bounding sphere radius */
64
- bounds_radius: f32,
65
-
66
63
  /** attribute word offset of world position (or {@link EMITTER_ATTRIBUTE_ABSENT}) */
67
64
  render_position: u32,
68
65
  /** attribute word offset of size */
@@ -1 +1 @@
1
- {"version":3,"file":"particle_emitter_record.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/particle_emitter_record.js"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,6CANW,MAAM,WACN,OAAO,+BAA+B,EAAE,eAAe,WACvD,OAAO,yCAAyC,EAAE,yBAAyB,GAEzE,MAAM,CA2ClB;AAED;;;;;GAKG;AACH,yCAFa,MAAM,CA8BlB"}
1
+ {"version":3,"file":"particle_emitter_record.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/particles/data/particle_emitter_record.js"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AAEH;;;;;;;;;;;;;;;;GAgBG;AACH,6CANW,MAAM,WACN,OAAO,+BAA+B,EAAE,eAAe,WACvD,OAAO,yCAAyC,EAAE,yBAAyB,GAEzE,MAAM,CAwClB;AAED;;;;;GAKG;AACH,yCAFa,MAAM,CA2BlB"}