@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
@@ -1,425 +1,424 @@
1
- import { assert } from "../../../../core/assert.js";
2
- import { is_typed_array_equals } from "../../../../core/collection/array/typed/is_typed_array_equals.js";
3
- import { float32_array_hash } from "../../../../core/collection/array/typed/float32_array_hash.js";
4
- import { hash_mix2 } from "../../../../core/math/hash/hash_mix2.js";
5
- import { hash_mix3 } from "../../../../core/math/hash/hash_mix3.js";
6
- import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
7
- import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
8
- import { Node3D } from "../../scene/Node3D.js";
9
- import { EMITTER_ATTRIBUTE_ABSENT, make_emitter_flags } from "../data/PARTICLE_EMITTER_STRUCT.js";
10
-
11
- /**
12
- * Render channels an emitter can bind to one of its effect's per-particle attributes. The names
13
- * match the `render_*` fields of {@link ../data/PARTICLE_EMITTER_STRUCT.js}.
14
- *
15
- * @type {string[]}
16
- */
17
- export const EMITTER_RENDER_CHANNELS = Object.freeze([
18
- "position", "size", "color", "rotation", "frame", "velocity",
19
- ]);
20
-
21
- /**
22
- * How many record words the render pass reads for each channel.
23
- *
24
- * A channel is read at a fixed width — `chunk_particle_billboard_vertex.js` takes four words for
25
- * colour and three for position, whatever the attribute behind the offset happens to be. Binding one
26
- * to a narrower attribute is therefore not a smaller read but a read into whatever the layout put
27
- * next, and nothing downstream objects: the particles simply come out wrong.
28
- *
29
- * Binding is deliberately not made to enforce this — the offsets are the record's, and an author who
30
- * knows what is packed where may mean it — but an editor has this table so it can say so.
31
- *
32
- * @type {Object<string, number>}
33
- */
34
- export const EMITTER_RENDER_CHANNEL_COMPONENTS = Object.freeze({
35
- position: 3,
36
- size: 1,
37
- color: 4,
38
- rotation: 1,
39
- frame: 1,
40
- velocity: 3,
41
- });
42
-
43
- /**
44
- * One particle emitter: a scene node that runs a compiled effect.
45
- *
46
- * Being a {@link Node3D} is what places it. Add it to a scene and it gets a row in the scene
47
- * database's `transforms` table like any other node; parent it to a joint and the GPU hierarchy
48
- * pass composes its world matrix from the joint's every frame, animation included, with nothing
49
- * copied through the CPU. Remove it from the scene and the row goes with it — and so do its
50
- * particles, within a frame, because the emitter's table row is retired with it and its particles
51
- * carry that row's generation. There is no transform of its own to keep in step with anything.
52
- *
53
- * The rest is a description of an effect and nothing else: the attribute {@link layout} and the
54
- * compiled {@link program}, the {@link texture} to draw with, spawn rate, blend / projection /
55
- * feature flags, bounds sphere, render-attribute bindings and flipbook grid. Every one of those is
56
- * the author's, means the same thing with no device present, and is what a saved effect is made of.
57
- *
58
- * **Nothing here is the GPU's.** Which table row the emitter was given, which tenancy of it, where
59
- * its program was placed in the program heap, which `transforms` row its node ended up in, where
60
- * its texture was packed into the atlas — all of that is
61
- * {@link ./GPUParticleEmitterContext.js GPUParticleEmitterContext}, held by the
62
- * {@link ./EmitterRegistry.js} that registered the emitter and reachable through
63
- * {@link ./EmitterRegistry.js#context}. An emitter no registry holds has none, which is also how
64
- * "is this emitter live?" is asked. Nor is anything the GPU integrates per frame here — spawn
65
- * accumulator, age, sleep are {@link ../data/PARTICLE_EMITTER_STATE.js}, and they stay on the GPU.
66
- *
67
- * Fields are plain data with no setters. After changing any of them, set `needsUpdate = true` — the
68
- * node's own change counter, the same one the renderer reads to decide whether a transform needs
69
- * re-uploading — and {@link ./EmitterRegistry.js#flush} restages the row on its next flush. One
70
- * counter rather than a setter per field because a row is restaged whole either way: one changed
71
- * field and ten cost the same upload.
72
- */
73
- export class ParticleEmitter extends Node3D {
74
-
75
- /**
76
- * The per-particle attribute schema: which named attributes a particle of this emitter has, how
77
- * wide each is, and what word of the record it sits at.
78
- *
79
- * @type {import("../layout/ParticleLayout.js").ParticleLayout}
80
- */
81
- layout;
82
-
83
- /**
84
- * The compiled INIT/UPDATE bytecode run for every particle of this emitter.
85
- *
86
- * @type {import("../isa/ParticleProgram.js").ParticleProgram}
87
- */
88
- program;
89
-
90
- /**
91
- * Image to draw the particles with, by URL, or `null` to draw against the default atlas — a
92
- * single white texel, so an untextured emitter is a flat quad of its colour.
93
- *
94
- * A URL rather than a texture, and no uv rect: the particle system packs every emitter's image
95
- * into one sheet so that a hundred effects sharing a puff of smoke pay for one image, and where
96
- * a given image lands in that sheet is the system's answer, not the author's — it changes on
97
- * every repack, without the emitter changing. The resolved patch is
98
- * {@link ./GPUParticleEmitterContext.js#atlas_region}.
99
- *
100
- * @type {string|null}
101
- */
102
- texture = null;
103
-
104
- /**
105
- * Base RNG seed.
106
- *
107
- * Two emitters running one program produce identical particles from identical seeds. `0` reads
108
- * as "unset", and the row written for such an emitter takes its registration's generation
109
- * instead, which no other emitter of that registry shares.
110
- *
111
- * @type {number}
112
- */
113
- seed = 0;
114
-
115
- /**
116
- * Continuous emission rate, particles per second. Integrated on the GPU by the emitter tick
117
- * pass; the CPU never counts particles.
118
- * @type {number}
119
- */
120
- spawn_rate = 0;
121
-
122
- /**
123
- * Packed feature / blend / projection bits — see {@link make_emitter_flags}.
124
- * @type {number}
125
- */
126
- flags = 0;
127
-
128
- /**
129
- * Bounding sphere in emitter-local space, scaled and placed by the node's world matrix.
130
- *
131
- * Two passes read it: the optional simulation culling (a culled emitter sleeps), and the AVBOIT
132
- * occupancy, which marks the depth slices this emitter's particles may reach. The second gives
133
- * it teeth even for an emitter that does not opt into culling — a particle outside the sphere
134
- * still draws, but its extinction lands in whatever physical slice the warp put nearest, so it
135
- * occludes at slightly the wrong depth. Size it to hold the effect.
136
- *
137
- * Hold the *quads*, not the centres. A camera-facing billboard has almost no depth extent, but
138
- * an axis-locked, world-oriented or velocity-stretched one reaches a particle size — or, when
139
- * stretched, several — in front of and behind the particle it belongs to.
140
- *
141
- * @type {Float32Array}
142
- */
143
- bounds_center = new Float32Array(3);
144
-
145
- /**
146
- * @type {number}
147
- */
148
- bounds_radius = 1;
149
-
150
- /**
151
- * Flipbook grid: columns, rows, over whatever region {@link texture} was packed into. `[1, 1]`
152
- * is a single frame.
153
- * @type {Float32Array}
154
- */
155
- flipbook = new Float32Array([1, 1]);
156
-
157
- /** @type {number} record word offset of world position, or {@link EMITTER_ATTRIBUTE_ABSENT} */
158
- render_position = EMITTER_ATTRIBUTE_ABSENT;
159
- /** @type {number} record word offset of size */
160
- render_size = EMITTER_ATTRIBUTE_ABSENT;
161
- /** @type {number} record word offset of rgba colour */
162
- render_color = EMITTER_ATTRIBUTE_ABSENT;
163
- /** @type {number} record word offset of roll angle, radians */
164
- render_rotation = EMITTER_ATTRIBUTE_ABSENT;
165
- /** @type {number} record word offset of flipbook frame index */
166
- render_frame = EMITTER_ATTRIBUTE_ABSENT;
167
- /** @type {number} record word offset of velocity, read by the STRETCHED projection */
168
- render_velocity = EMITTER_ATTRIBUTE_ABSENT;
169
-
170
- get type() {
171
- return "ParticleEmitter";
172
- }
173
-
174
- /**
175
- * Bind render channels to per-particle attributes of this emitter's effect, by attribute name.
176
- * Channels left out of `channels` keep whatever they had; a channel that was never bound stays
177
- * at {@link EMITTER_ATTRIBUTE_ABSENT}, which the passes read as "this emitter has no such
178
- * attribute" — the render pass substitutes a default, the sort pass treats the particle as
179
- * being at the origin.
180
- *
181
- * The record stores word offsets rather than names because that is what the render and sort
182
- * passes index a particle record with; resolving here is the only place a name is involved.
183
- *
184
- * @param {Object<string,string>} channels channel name (see {@link EMITTER_RENDER_CHANNELS}) ->
185
- * attribute name in the effect's layout
186
- */
187
- bind_render(channels) {
188
- assert.isObject(channels, 'channels');
189
- assert.defined(this.layout, 'layout');
190
-
191
- const layout = this.layout;
192
-
193
- for (const channel of Object.keys(channels)) {
194
- const offset = layout.offsetOf(channels[channel]);
195
-
196
- switch (channel) {
197
- case "position": this.render_position = offset; break;
198
- case "size": this.render_size = offset; break;
199
- case "color": this.render_color = offset; break;
200
- case "rotation": this.render_rotation = offset; break;
201
- case "frame": this.render_frame = offset; break;
202
- case "velocity": this.render_velocity = offset; break;
203
- default:
204
- throw new Error(
205
- `Unknown render channel '${channel}', expected one of ${EMITTER_RENDER_CHANNELS.join(', ')}`
206
- );
207
- }
208
- }
209
-
210
- this.needsUpdate = true;
211
- }
212
-
213
- /**
214
- * Structural equality over everything an emitter *is*: where it sits, what it runs, what it
215
- * draws with, and how it is bound to the renderer. Two emitters that compare equal describe one
216
- * effect in one place, and one row would serve both.
217
- *
218
- * The node's identity is not part of it — `id`, parent and children say where an emitter is in
219
- * a scene, not what it is — and neither is anything the GPU assigned it, which is not here to
220
- * be compared. {@link transform_global} is left out too, being derived from
221
- * {@link transform_local} and the parent chain.
222
- *
223
- * @param {ParticleEmitter} other
224
- * @returns {boolean}
225
- */
226
- equals(other) {
227
- if (this === other) {
228
- return true;
229
- }
230
-
231
- return this.name === other.name
232
- && this.texture === other.texture
233
- && this.seed === other.seed
234
- && this.spawn_rate === other.spawn_rate
235
- && this.flags === other.flags
236
- && this.bounds_radius === other.bounds_radius
237
- && this.render_position === other.render_position
238
- && this.render_size === other.render_size
239
- && this.render_color === other.render_color
240
- && this.render_rotation === other.render_rotation
241
- && this.render_frame === other.render_frame
242
- && this.render_velocity === other.render_velocity
243
- && is_typed_array_equals(this.bounds_center, other.bounds_center)
244
- && is_typed_array_equals(this.flipbook, other.flipbook)
245
- && this.transform_local.equals(other.transform_local)
246
- && this.layout.equals(other.layout)
247
- && this.program.equals(other.program);
248
- }
249
-
250
- /**
251
- * Hash over everything {@link equals} compares, in the same terms — the compiled program hashes
252
- * by its own bytecode rather than by object identity, so two emitters separately compiled from
253
- * one graph hash equally, as they compare equally.
254
- *
255
- * @returns {number} int32
256
- */
257
- hash() {
258
- const bindings = hash_mix3(
259
- hash_mix3(this.render_position, this.render_size, this.render_color),
260
- hash_mix3(this.render_rotation, this.render_frame, this.render_velocity),
261
- this.flags
262
- );
263
-
264
- const geometry = hash_mix3(
265
- float32_array_hash(this.bounds_center, 0, this.bounds_center.length),
266
- computeHashFloat(this.bounds_radius),
267
- float32_array_hash(this.flipbook, 0, this.flipbook.length)
268
- );
269
-
270
- const appearance = hash_mix3(
271
- computeStringHash(this.name),
272
- computeStringHash(this.texture),
273
- hash_mix2(this.seed, computeHashFloat(this.spawn_rate))
274
- );
275
-
276
- return hash_mix3(
277
- hash_mix2(bindings, geometry),
278
- hash_mix2(appearance, this.transform_local.hash()),
279
- hash_mix2(this.layout.hash(), this.program.hash())
280
- );
281
- }
282
-
283
- /**
284
- * Make this emitter a copy of `other`: same effect, same settings, same placement.
285
- *
286
- * The layout and the compiled program are shared rather than duplicated — both are immutable
287
- * once compiled, and sharing them is what puts two emitters of one effect on one program id in
288
- * the program heap, hence in one simulation bucket.
289
- *
290
- * What is not copied is the node's identity and its place in a scene ({@link Node3D#id},
291
- * parent, children), and anything a registry gave the original: a copy is a second emitter, not
292
- * a second reference to the first one's table row.
293
- *
294
- * @param {ParticleEmitter} other
295
- */
296
- copy(other) {
297
- super.copy(other);
298
-
299
- this.name = other.name;
300
-
301
- this.layout = other.layout;
302
- this.program = other.program;
303
- this.texture = other.texture;
304
-
305
- this.seed = other.seed;
306
- this.spawn_rate = other.spawn_rate;
307
- this.flags = other.flags;
308
-
309
- this.bounds_center.set(other.bounds_center);
310
- this.bounds_radius = other.bounds_radius;
311
- this.flipbook.set(other.flipbook);
312
-
313
- this.render_position = other.render_position;
314
- this.render_size = other.render_size;
315
- this.render_color = other.render_color;
316
- this.render_rotation = other.render_rotation;
317
- this.render_frame = other.render_frame;
318
- this.render_velocity = other.render_velocity;
319
- }
320
-
321
- /**
322
- * A second emitter equal to this one, and registered with nothing. Its subtree is cloned the
323
- * way {@link Node3D#clone} clones one — overridden only because the base builds a plain
324
- * `Node3D`, which would keep the transform and drop the effect.
325
- *
326
- * @returns {ParticleEmitter}
327
- */
328
- clone() {
329
- const r = new ParticleEmitter();
330
-
331
- r.copy(this);
332
-
333
- for (const child of this.children) {
334
- const clone = child.clone();
335
-
336
- clone.parent = r;
337
-
338
- r.children.push(clone);
339
- }
340
-
341
- return r;
342
- }
343
-
344
- /**
345
- * Build an emitter from a plain description. Every parameter is optional except the compiled
346
- * effect — an emitter with no program has nothing to run.
347
- *
348
- * `layout` and `program` are the two halves of what {@link ./create_particle_effect.js} returns,
349
- * so an effect goes in by spreading it: `ParticleEmitter.from({ ...effect, spawn_rate: 100 })`.
350
- *
351
- * @param {object} config
352
- * @param {import("../layout/ParticleLayout.js").ParticleLayout} config.layout per-particle
353
- * attribute schema
354
- * @param {import("../isa/ParticleProgram.js").ParticleProgram} config.program compiled INIT /
355
- * UPDATE bytecode
356
- * @param {ArrayLike<number>} [config.position] local translation, `[x, y, z]`
357
- * @param {string|null} [config.texture] image URL to draw with
358
- * @param {number} [config.seed]
359
- * @param {number} [config.spawn_rate] particles/second
360
- * @param {object} [config.flags] passed to {@link make_emitter_flags}
361
- * @param {{center?: ArrayLike<number>, radius?: number}} [config.bounds]
362
- * @param {Object<string,string>} [config.render] render channel -> attribute name
363
- * @param {ArrayLike<number>} [config.flipbook] [cols, rows]
364
- * @param {string} [config.name]
365
- * @returns {ParticleEmitter}
366
- */
367
- static from(config) {
368
- assert.isObject(config, 'config');
369
- assert.defined(config.layout, 'config.layout');
370
- assert.defined(config.program, 'config.program');
371
-
372
- const emitter = new ParticleEmitter();
373
-
374
- emitter.layout = config.layout;
375
- emitter.program = config.program;
376
-
377
- if (config.name !== undefined) {
378
- emitter.name = config.name;
379
- }
380
-
381
- if (config.texture !== undefined) {
382
- emitter.texture = config.texture;
383
- }
384
-
385
- if (config.position !== undefined) {
386
- emitter.transform_local.setTranslation(config.position[0], config.position[1], config.position[2]);
387
- emitter.updateMatrices();
388
- }
389
-
390
- if (config.seed !== undefined) {
391
- emitter.seed = config.seed;
392
- }
393
-
394
- if (config.spawn_rate !== undefined) {
395
- emitter.spawn_rate = config.spawn_rate;
396
- }
397
-
398
- emitter.flags = make_emitter_flags(config.flags ?? {});
399
-
400
- if (config.bounds !== undefined) {
401
- if (config.bounds.center !== undefined) {
402
- emitter.bounds_center.set(config.bounds.center);
403
- }
404
- if (config.bounds.radius !== undefined) {
405
- emitter.bounds_radius = config.bounds.radius;
406
- }
407
- }
408
-
409
- if (config.flipbook !== undefined) {
410
- emitter.flipbook.set(config.flipbook);
411
- }
412
-
413
- if (config.render !== undefined) {
414
- emitter.bind_render(config.render);
415
- }
416
-
417
- return emitter;
418
- }
419
- }
420
-
421
- /**
422
- * @readonly
423
- * @type {boolean}
424
- */
425
- ParticleEmitter.prototype.isParticleEmitter = true;
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { is_typed_array_equals } from "../../../../core/collection/array/typed/is_typed_array_equals.js";
3
+ import { float32_array_hash } from "../../../../core/collection/array/typed/float32_array_hash.js";
4
+ import { hash_mix2 } from "../../../../core/math/hash/hash_mix2.js";
5
+ import { hash_mix3 } from "../../../../core/math/hash/hash_mix3.js";
6
+ import { computeHashFloat } from "../../../../core/primitives/numbers/computeHashFloat.js";
7
+ import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
8
+ import { Node3D } from "../../scene/Node3D.js";
9
+ import { EMITTER_ATTRIBUTE_ABSENT, make_emitter_flags } from "../data/PARTICLE_EMITTER_STRUCT.js";
10
+
11
+ /**
12
+ * Render channels an emitter can bind to one of its effect's per-particle attributes. The names
13
+ * match the `render_*` fields of {@link ../data/PARTICLE_EMITTER_STRUCT.js}.
14
+ *
15
+ * @type {string[]}
16
+ */
17
+ export const EMITTER_RENDER_CHANNELS = Object.freeze([
18
+ "position", "size", "color", "rotation", "frame", "velocity",
19
+ ]);
20
+
21
+ /**
22
+ * How many record words the render pass reads for each channel.
23
+ *
24
+ * A channel is read at a fixed width — `chunk_particle_billboard_vertex.js` takes four words for
25
+ * colour and three for position, whatever the attribute behind the offset happens to be. Binding one
26
+ * to a narrower attribute is therefore not a smaller read but a read into whatever the layout put
27
+ * next, and nothing downstream objects: the particles simply come out wrong.
28
+ *
29
+ * Binding is deliberately not made to enforce this — the offsets are the record's, and an author who
30
+ * knows what is packed where may mean it — but an editor has this table so it can say so.
31
+ *
32
+ * @type {Object<string, number>}
33
+ */
34
+ export const EMITTER_RENDER_CHANNEL_COMPONENTS = Object.freeze({
35
+ position: 3,
36
+ size: 1,
37
+ color: 4,
38
+ rotation: 1,
39
+ frame: 1,
40
+ velocity: 3,
41
+ });
42
+
43
+ /**
44
+ * One particle emitter: a scene node that runs a compiled effect.
45
+ *
46
+ * Being a {@link Node3D} is what places it. Add it to a scene and it gets a row in the scene
47
+ * database's `transforms` table like any other node; parent it to a joint and the GPU hierarchy
48
+ * pass composes its world matrix from the joint's every frame, animation included, with nothing
49
+ * copied through the CPU. Remove it from the scene and the row goes with it — and so do its
50
+ * particles, within a frame, because the emitter's table row is retired with it and its particles
51
+ * carry that row's generation. There is no transform of its own to keep in step with anything.
52
+ *
53
+ * The rest is a description of an effect and nothing else: the attribute {@link layout} and the
54
+ * compiled {@link program}, the {@link texture} to draw with, spawn rate, pre-warm duration, blend /
55
+ * projection / feature flags, render-attribute bindings and flipbook grid. Every one of those is the author's,
56
+ * means the same thing with no device present, and is what a saved effect is made of.
57
+ *
58
+ * There are no bounds among them, and there is nowhere here they could go. An emitter's particles
59
+ * are simulated in world space by arbitrary bytecode: they go where the program sends them, at
60
+ * whatever size it gives them, for as long as it keeps them alive. Nothing an author could write
61
+ * down would bound that except "infinite", which culls nothing and marks every depth slice. So the
62
+ * box is measured instead — from the live particles, by a pass of its own at the end of every frame
63
+ * (`../bounds/`) — and it lives with the rest of the measured state in
64
+ * {@link ../data/PARTICLE_EMITTER_STATE.js}. The one thing that IS the author's is whether the
65
+ * emitter opts into culling at all: `EMITTER_FLAG.CULL` in {@link flags}.
66
+ *
67
+ * **Nothing here is the GPU's.** Which table row the emitter was given, which tenancy of it, where
68
+ * its program was placed in the program heap, which `transforms` row its node ended up in, where
69
+ * its texture was packed into the atlas — all of that is
70
+ * {@link ./GPUParticleEmitterContext.js GPUParticleEmitterContext}, held by the
71
+ * {@link ./EmitterRegistry.js} that registered the emitter and reachable through
72
+ * {@link ./EmitterRegistry.js#context}. An emitter no registry holds has none, which is also how
73
+ * "is this emitter live?" is asked. Nor is anything the GPU integrates per frame here — spawn
74
+ * accumulator, age, sleep are {@link ../data/PARTICLE_EMITTER_STATE.js}, and they stay on the GPU.
75
+ *
76
+ * Fields are plain data with no setters. After changing any of them, set `needsUpdate = true` — the
77
+ * node's own change counter, the same one the renderer reads to decide whether a transform needs
78
+ * re-uploading — and {@link ./EmitterRegistry.js#flush} restages the row on its next flush. One
79
+ * counter rather than a setter per field because a row is restaged whole either way: one changed
80
+ * field and ten cost the same upload.
81
+ */
82
+ export class ParticleEmitter extends Node3D {
83
+
84
+ /**
85
+ * The per-particle attribute schema: which named attributes a particle of this emitter has, how
86
+ * wide each is, and what word of the record it sits at.
87
+ *
88
+ * @type {import("../layout/ParticleLayout.js").ParticleLayout}
89
+ */
90
+ layout;
91
+
92
+ /**
93
+ * The compiled INIT/UPDATE bytecode run for every particle of this emitter.
94
+ * Replace the immutable program to edit it; do not mutate its bytecode in place.
95
+ *
96
+ * @type {import("../isa/ParticleProgram.js").ParticleProgram}
97
+ */
98
+ program;
99
+
100
+ /**
101
+ * Image to draw the particles with, by URL, or `null` to draw against the default atlas — a
102
+ * single white texel, so an untextured emitter is a flat quad of its colour.
103
+ *
104
+ * The ECS GPUParticleEmitterSystem loads this URL through AssetManager and packs the image
105
+ * into a shared sheet. Standalone callers provide their own atlas and regions. Where
106
+ * a given image lands in that sheet is the system's answer, not the author's — it changes on
107
+ * every repack, without the emitter changing. The resolved patch is
108
+ * {@link ./GPUParticleEmitterContext.js#atlas_region}.
109
+ *
110
+ * @type {string|null}
111
+ */
112
+ texture = null;
113
+
114
+ /**
115
+ * Base RNG seed.
116
+ *
117
+ * Two emitters running one program produce identical particles from identical seeds. `0` reads
118
+ * as "unset", and the row written for such an emitter takes its registration's generation
119
+ * instead, which no other emitter of that registry shares.
120
+ *
121
+ * @type {number}
122
+ */
123
+ seed = 0;
124
+
125
+ /**
126
+ * Continuous emission rate, particles per second. Integrated on the GPU by the emitter tick
127
+ * pass; the CPU never counts particles.
128
+ * @type {number}
129
+ */
130
+ spawn_rate = 0;
131
+
132
+ /**
133
+ * Seconds this emitter is run forward before it is first shown, `0` for none.
134
+ *
135
+ * A continuous effect — a smoke column, a dust field — starts empty and grows into its steady
136
+ * state over one particle lifetime, in front of whoever is watching. Setting this to about the
137
+ * effect's particle lifetime means the population that arrives on the first visible frame is the
138
+ * one the emitter would have had by then: spawned, aged, and thinned by its own program over
139
+ * that long, rather than a crowd of newborns.
140
+ *
141
+ * The author's number, and the only thing about a warm-up that is authored. It is not written to
142
+ * the GPU record: the system queues an emitter that has one at the moment it is registered, and
143
+ * the warm-up pipeline (`../warmup/`) runs its simulation forward on that frame, over a
144
+ * population of its own, before moving the result into the scene. Longer costs a longer loop on
145
+ * that one frame and nothing after it.
146
+ *
147
+ * Prewarm controls the initial population's age distribution. It is not needed to bootstrap
148
+ * culling: emitters without a measured population have unknown bounds and remain eligible to spawn.
149
+ *
150
+ * @type {number}
151
+ */
152
+ prewarm = 0;
153
+
154
+ /**
155
+ * Packed feature / blend / projection bits — see {@link make_emitter_flags}.
156
+ * @type {number}
157
+ */
158
+ flags = 0;
159
+
160
+ /**
161
+ * Flipbook grid: columns, rows, over whatever region {@link texture} was packed into. `[1, 1]`
162
+ * is a single frame.
163
+ * @type {Float32Array}
164
+ */
165
+ flipbook = new Float32Array([1, 1]);
166
+
167
+ /** @type {number} record word offset of world position, or {@link EMITTER_ATTRIBUTE_ABSENT} */
168
+ render_position = EMITTER_ATTRIBUTE_ABSENT;
169
+ /** @type {number} record word offset of size */
170
+ render_size = EMITTER_ATTRIBUTE_ABSENT;
171
+ /** @type {number} record word offset of rgba colour */
172
+ render_color = EMITTER_ATTRIBUTE_ABSENT;
173
+ /** @type {number} record word offset of roll angle, radians */
174
+ render_rotation = EMITTER_ATTRIBUTE_ABSENT;
175
+ /** @type {number} record word offset of flipbook frame index */
176
+ render_frame = EMITTER_ATTRIBUTE_ABSENT;
177
+ /** @type {number} record word offset of velocity, read by the STRETCHED projection */
178
+ render_velocity = EMITTER_ATTRIBUTE_ABSENT;
179
+
180
+ get type() {
181
+ return "ParticleEmitter";
182
+ }
183
+
184
+ /**
185
+ * Bind render channels to per-particle attributes of this emitter's effect, by attribute name.
186
+ * Channels left out of `channels` keep whatever they had; a channel that was never bound stays
187
+ * at {@link EMITTER_ATTRIBUTE_ABSENT}, which the passes read as "this emitter has no such
188
+ * attribute" — the render pass substitutes a default, the sort pass treats the particle as
189
+ * being at the origin.
190
+ *
191
+ * The record stores word offsets rather than names because that is what the render and sort
192
+ * passes index a particle record with; resolving here is the only place a name is involved.
193
+ *
194
+ * @param {Object<string,string>} channels channel name (see {@link EMITTER_RENDER_CHANNELS}) ->
195
+ * attribute name in the effect's layout
196
+ */
197
+ bind_render(channels) {
198
+ assert.isObject(channels, 'channels');
199
+ assert.defined(this.layout, 'layout');
200
+
201
+ const layout = this.layout;
202
+
203
+ for (const channel of Object.keys(channels)) {
204
+ const offset = layout.offsetOf(channels[channel]);
205
+
206
+ switch (channel) {
207
+ case "position": this.render_position = offset; break;
208
+ case "size": this.render_size = offset; break;
209
+ case "color": this.render_color = offset; break;
210
+ case "rotation": this.render_rotation = offset; break;
211
+ case "frame": this.render_frame = offset; break;
212
+ case "velocity": this.render_velocity = offset; break;
213
+ default:
214
+ throw new Error(
215
+ `Unknown render channel '${channel}', expected one of ${EMITTER_RENDER_CHANNELS.join(', ')}`
216
+ );
217
+ }
218
+ }
219
+
220
+ this.needsUpdate = true;
221
+ }
222
+
223
+ /**
224
+ * Structural equality over everything an emitter *is*: where it sits, what it runs, what it
225
+ * draws with, and how it is bound to the renderer. Two emitters that compare equal describe one
226
+ * effect in one place, and one row would serve both.
227
+ *
228
+ * The node's identity is not part of it — `id`, parent and children say where an emitter is in
229
+ * a scene, not what it is — and neither is anything the GPU assigned it, which is not here to
230
+ * be compared. {@link transform_global} is left out too, being derived from
231
+ * {@link transform_local} and the parent chain.
232
+ *
233
+ * @param {ParticleEmitter} other
234
+ * @returns {boolean}
235
+ */
236
+ equals(other) {
237
+ if (this === other) {
238
+ return true;
239
+ }
240
+
241
+ return this.name === other.name
242
+ && this.texture === other.texture
243
+ && this.seed === other.seed
244
+ && this.spawn_rate === other.spawn_rate
245
+ && this.prewarm === other.prewarm
246
+ && this.flags === other.flags
247
+ && this.render_position === other.render_position
248
+ && this.render_size === other.render_size
249
+ && this.render_color === other.render_color
250
+ && this.render_rotation === other.render_rotation
251
+ && this.render_frame === other.render_frame
252
+ && this.render_velocity === other.render_velocity
253
+ && is_typed_array_equals(this.flipbook, other.flipbook)
254
+ && this.transform_local.equals(other.transform_local)
255
+ && this.layout.equals(other.layout)
256
+ && this.program.equals(other.program);
257
+ }
258
+
259
+ /**
260
+ * Hash over everything {@link equals} compares, in the same terms — the compiled program hashes
261
+ * by its own bytecode rather than by object identity, so two emitters separately compiled from
262
+ * one graph hash equally, as they compare equally.
263
+ *
264
+ * @returns {number} int32
265
+ */
266
+ hash() {
267
+ const bindings = hash_mix3(
268
+ hash_mix3(this.render_position, this.render_size, this.render_color),
269
+ hash_mix3(this.render_rotation, this.render_frame, this.render_velocity),
270
+ this.flags
271
+ );
272
+
273
+ const flipbook = float32_array_hash(this.flipbook, 0, this.flipbook.length);
274
+
275
+ const appearance = hash_mix3(
276
+ computeStringHash(this.name),
277
+ computeStringHash(this.texture),
278
+ hash_mix3(this.seed, computeHashFloat(this.spawn_rate), computeHashFloat(this.prewarm))
279
+ );
280
+
281
+ return hash_mix3(
282
+ hash_mix2(bindings, flipbook),
283
+ hash_mix2(appearance, this.transform_local.hash()),
284
+ hash_mix2(this.layout.hash(), this.program.hash())
285
+ );
286
+ }
287
+
288
+ /**
289
+ * Make this emitter a copy of `other`: same effect, same settings, same placement.
290
+ *
291
+ * The layout and the compiled program are shared rather than duplicated — both are immutable
292
+ * once compiled, and sharing them is what puts two emitters of one effect on one program id in
293
+ * the program heap, hence in one simulation bucket.
294
+ *
295
+ * What is not copied is the node's identity and its place in a scene ({@link Node3D#id},
296
+ * parent, children), and anything a registry gave the original: a copy is a second emitter, not
297
+ * a second reference to the first one's table row.
298
+ *
299
+ * @param {ParticleEmitter} other
300
+ */
301
+ copy(other) {
302
+ super.copy(other);
303
+
304
+ this.name = other.name;
305
+
306
+ this.layout = other.layout;
307
+ this.program = other.program;
308
+ this.texture = other.texture;
309
+
310
+ this.seed = other.seed;
311
+ this.spawn_rate = other.spawn_rate;
312
+ this.prewarm = other.prewarm;
313
+ this.flags = other.flags;
314
+
315
+ this.flipbook.set(other.flipbook);
316
+
317
+ this.render_position = other.render_position;
318
+ this.render_size = other.render_size;
319
+ this.render_color = other.render_color;
320
+ this.render_rotation = other.render_rotation;
321
+ this.render_frame = other.render_frame;
322
+ this.render_velocity = other.render_velocity;
323
+ }
324
+
325
+ /**
326
+ * A second emitter equal to this one, and registered with nothing. Its subtree is cloned the
327
+ * way {@link Node3D#clone} clones one — overridden only because the base builds a plain
328
+ * `Node3D`, which would keep the transform and drop the effect.
329
+ *
330
+ * @returns {ParticleEmitter}
331
+ */
332
+ clone() {
333
+ const r = new ParticleEmitter();
334
+
335
+ r.copy(this);
336
+
337
+ for (const child of this.children) {
338
+ const clone = child.clone();
339
+
340
+ clone.parent = r;
341
+
342
+ r.children.push(clone);
343
+ }
344
+
345
+ return r;
346
+ }
347
+
348
+ /**
349
+ * Build an emitter from a plain description. Every parameter is optional except the compiled
350
+ * effect — an emitter with no program has nothing to run.
351
+ *
352
+ * `layout` and `program` are the two halves of what {@link ./create_particle_effect.js} returns,
353
+ * so an effect goes in by spreading it: `ParticleEmitter.from({ ...effect, spawn_rate: 100 })`.
354
+ *
355
+ * @param {object} config
356
+ * @param {import("../layout/ParticleLayout.js").ParticleLayout} config.layout per-particle
357
+ * attribute schema
358
+ * @param {import("../isa/ParticleProgram.js").ParticleProgram} config.program compiled INIT /
359
+ * UPDATE bytecode
360
+ * @param {ArrayLike<number>} [config.position] local translation, `[x, y, z]`
361
+ * @param {string|null} [config.texture] image URL to draw with
362
+ * @param {number} [config.seed]
363
+ * @param {number} [config.spawn_rate] particles/second
364
+ * @param {number} [config.prewarm] seconds of emission to run forward before the first frame
365
+ * @param {object} [config.flags] passed to {@link make_emitter_flags}
366
+ * @param {Object<string,string>} [config.render] render channel -> attribute name
367
+ * @param {ArrayLike<number>} [config.flipbook] [cols, rows]
368
+ * @param {string} [config.name]
369
+ * @returns {ParticleEmitter}
370
+ */
371
+ static from(config) {
372
+ assert.isObject(config, 'config');
373
+ assert.defined(config.layout, 'config.layout');
374
+ assert.defined(config.program, 'config.program');
375
+
376
+ const emitter = new ParticleEmitter();
377
+
378
+ emitter.layout = config.layout;
379
+ emitter.program = config.program;
380
+
381
+ if (config.name !== undefined) {
382
+ emitter.name = config.name;
383
+ }
384
+
385
+ if (config.texture !== undefined) {
386
+ emitter.texture = config.texture;
387
+ }
388
+
389
+ if (config.position !== undefined) {
390
+ emitter.transform_local.setTranslation(config.position[0], config.position[1], config.position[2]);
391
+ emitter.updateMatrices();
392
+ }
393
+
394
+ if (config.seed !== undefined) {
395
+ emitter.seed = config.seed;
396
+ }
397
+
398
+ if (config.spawn_rate !== undefined) {
399
+ emitter.spawn_rate = config.spawn_rate;
400
+ }
401
+
402
+ if (config.prewarm !== undefined) {
403
+ emitter.prewarm = config.prewarm;
404
+ }
405
+
406
+ emitter.flags = make_emitter_flags(config.flags ?? {});
407
+
408
+ if (config.flipbook !== undefined) {
409
+ emitter.flipbook.set(config.flipbook);
410
+ }
411
+
412
+ if (config.render !== undefined) {
413
+ emitter.bind_render(config.render);
414
+ }
415
+
416
+ return emitter;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * @readonly
422
+ * @type {boolean}
423
+ */
424
+ ParticleEmitter.prototype.isParticleEmitter = true;