@woosh/meep-engine 3.60.4 → 3.60.5

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 (379) hide show
  1. package/CHANGELOG.md +2602 -0
  2. package/CONTRIBUTING.md +7 -0
  3. package/MIGRATION_2_TO_3.md +136 -0
  4. package/SECURITY.md +4 -0
  5. package/editor/EditorCameraNavigation.d.ts +2 -1
  6. package/editor/EditorCameraNavigation.d.ts.map +1 -1
  7. package/editor/EditorCameraNavigation.js +51 -57
  8. package/package.json +6 -2
  9. package/src/engine/EngineHarness.d.ts +3 -9
  10. package/src/engine/EngineHarness.d.ts.map +1 -1
  11. package/src/engine/EngineHarness.js +6 -8
  12. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
  13. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  14. package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
  15. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
  16. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
  17. package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
  18. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
  19. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
  20. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
  21. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
  22. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  23. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
  24. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
  25. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  26. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
  27. package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
  28. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  29. package/src/engine/graphics/ecs/water/Water.js +8 -0
  30. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  31. package/src/engine/graphics3/WaterSystem.js +731 -734
  32. package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
  33. package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
  34. package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
  35. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
  36. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  37. package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
  38. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
  39. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
  40. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
  41. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
  42. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
  43. package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
  44. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
  45. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  46. package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
  47. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
  48. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
  49. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
  50. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
  51. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
  52. package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
  53. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
  54. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
  55. package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
  56. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
  57. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
  58. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
  59. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  60. package/src/shade/playground/water_refraction/README.md +5 -6
  61. package/src/shade/playground/water_refraction/index.html +1 -1
  62. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  63. package/src/shade/renderer/Renderer.js +7 -5
  64. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  65. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  66. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  67. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  68. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  69. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  70. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  71. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  72. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -2
  73. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +2 -2
  74. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +2 -1
  75. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  76. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +18 -1
  77. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  78. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  79. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  80. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  81. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  82. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  83. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  84. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  85. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  86. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +1 -8
  87. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +5 -5
  88. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  89. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +10 -10
  90. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +1 -4
  91. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  92. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +1 -10
  93. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  94. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  95. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  96. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  97. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  98. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  99. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  100. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  101. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  102. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  103. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  104. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  105. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  106. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  107. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  108. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  109. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  110. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  111. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  112. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  113. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  114. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  115. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  116. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  117. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  118. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  119. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  120. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  121. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  122. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  123. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  124. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  125. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  126. package/src/shade/renderer/material/GPUMaterialManager.d.ts +7 -0
  127. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  128. package/src/shade/renderer/material/GPUMaterialManager.js +11 -0
  129. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  130. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  131. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  132. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  133. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  134. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  135. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  136. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  137. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  138. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  139. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  140. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  141. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  142. package/src/shade/renderer/water/shader_water.js +22 -13
  143. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  144. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  145. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  146. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  147. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  148. package/editor/REWORK_PLAN_2026_08.md +0 -692
  149. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  150. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  151. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  152. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  153. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  154. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  155. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  156. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  157. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  158. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  159. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  160. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  161. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  162. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  163. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  164. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  165. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  166. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  167. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  168. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  169. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  170. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  171. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  172. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  173. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  174. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  175. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  176. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  177. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  178. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  179. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  180. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  181. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  182. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  183. package/src/REVIEW_2026_08_06/generation.md +0 -176
  184. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  185. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  186. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  187. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  188. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  189. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  190. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  191. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  192. package/src/REVIEW_2026_08_06/view.md +0 -667
  193. package/src/REVIEW_2026_08_06.md +0 -610
  194. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  195. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  196. package/src/core/bvh8/NOTES.md +0 -63
  197. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  198. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  199. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  200. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  201. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  202. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  203. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  204. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  205. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  206. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  207. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  208. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  209. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  210. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  211. package/src/engine/REVIEW_2026_07_03.md +0 -211
  212. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  213. package/src/engine/control/first-person/DESIGN.md +0 -658
  214. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  215. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  216. package/src/engine/control/first-person/TODO.md +0 -160
  217. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  218. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  219. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  220. package/src/engine/ecs/ik/PLAN.md +0 -179
  221. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  222. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  223. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  224. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  225. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  226. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  227. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  228. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  229. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  230. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  231. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  232. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  233. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  234. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  235. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  236. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  237. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  238. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  239. package/src/engine/physics/cloth/PLAN.md +0 -3056
  240. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  241. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  242. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  243. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  244. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  245. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  246. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  247. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  248. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  249. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  250. package/src/format/image/avif/DECISIONS.md +0 -545
  251. package/src/format/image/jpeg/NOTES.md +0 -3
  252. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  253. package/src/generation/COORDINATES.md +0 -54
  254. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  255. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  256. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  257. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  258. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  259. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  260. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  261. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  262. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  263. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  264. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  265. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  266. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  267. package/src/shade/RENDERER_CONTRACT.md +0 -697
  268. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  269. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  270. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  271. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  272. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  273. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  274. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  275. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  276. package/src/shade/renderer/NOTES.md +0 -78
  277. package/src/shade/renderer/PLAN.md +0 -8
  278. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  279. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  280. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  281. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  282. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  283. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  284. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  285. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  286. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  287. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  288. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  289. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  290. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  291. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  292. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  293. package/src/shade/renderer/camera/orbital/README.md +0 -81
  294. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  295. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  296. package/src/shade/renderer/camera/orbital/index.js +0 -9
  297. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  298. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  299. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  300. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  301. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  302. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  303. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  304. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  305. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  306. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -481
  307. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  308. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  309. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  310. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  311. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  312. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  313. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  314. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -529
  315. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  316. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4306
  317. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  318. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  319. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  320. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  321. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  322. package/src/shade/renderer/hiz/NOTES.md +0 -3
  323. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  324. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  325. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  326. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  327. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  328. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  329. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  330. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  331. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  332. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -280
  333. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  334. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  335. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  336. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  337. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  338. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -332
  339. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  340. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  341. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  342. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  343. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  344. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  345. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  346. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  347. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  348. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  349. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  350. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  351. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -391
  352. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -153
  353. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  354. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  355. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  356. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  357. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  358. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  359. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  360. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  361. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  362. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -927
  363. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  364. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  365. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  366. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  367. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  368. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  369. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  370. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  371. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  372. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  373. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -3
  374. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  375. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  376. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  377. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  378. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  379. package/src/shade/wgsl/NOTES.md +0 -4
@@ -1,261 +0,0 @@
1
- # Plan — Ray-traced reflection IR (probe-baked geometry reverb)
2
-
3
- > The keystone upgrade: replace `bakeReverb`'s single-hit mean-free-path heuristic with a true
4
- > ray-traced, time-binned, **per-band** reflection IR, reusing the engine's Monte-Carlo path tracer.
5
- > Closes the Steam-Audio *reflections* gap (early reflections + room timbre), replaces the hand-tuned
6
- > `DECAY_PER_METRE` constant with measured per-band RT60, and makes `scattering` / `absorption[3]` live
7
- > data.
8
- >
9
- > **Status (2026-06-20): COMPLETE — the keystone is LANDED + committed + green.** The foundation
10
- > (**P1a/P1b** shape-raycast index, the **§0.5** `RowFirstTable`/`CSRGraph`/BVH data structures, the **P4
11
- > TLAS**) and now the reflection-IR bake itself — **P1c → P2 → P3** — are all in. `bakeReverb`'s single-hit
12
- > heuristic + `DECAY_PER_METRE` are gone; reverb RT60 is now *measured* per band from multi-bounce ray
13
- > tracing, validated against an analytic Sabine box, and rendered as a per-band decaying-noise IR.
14
- >
15
- > **One deviation from this plan, for correctness:** the trace terminates by ENERGY THRESHOLD, **not**
16
- > Russian roulette (§1 step 5 / §4 said RR). Fixed-probability RR is the wrong estimator for acoustic
17
- > reflectance — a survivor at bounce *n* carries energy `(1−α)^n` but is scaled by `2^n`, so for a live
18
- > room (`1−α > ½`) the survivor weight grows as `(2(1−α))^n` and rare deep survivors swamp exactly the
19
- > late bins the RT60 slope is fitted to. Deterministic decay-to-floor is unbiased for the fit window and
20
- > far lower variance.
21
-
22
- The structural blueprint is **`engine/graphics/sh3/lpv/LightProbeVolumeBaker.js`** + its
23
- `PathTracerProbeRenderer` — a near-1:1 analogue (probe-volume baking against scene geometry via the path
24
- tracer). We mirror its baker ↔ probe-renderer split and `TaskGroup` orchestration; we swap the
25
- accumulator from SH-of-radiance to **energy-over-time per band**.
26
-
27
- ---
28
-
29
- ## 0. What is baked vs. runtime — the source-agnostic guarantee (answers the dynamic-sources question)
30
-
31
- **The bake NEVER encodes sound sources. It encodes SPACE: (a) per-region reverberation profile and
32
- (b) region connectivity.** Sources are 100% runtime. There is no restriction to static/fixed sources.
33
-
34
- | Concern | Baked? | What's baked | Source / geometry dynamics |
35
- |---|---|---|---|
36
- | Direct path: occlusion, transmission, air, distance | **never** — real-time ray-trace every frame against the live `AcousticOccluderIndex` | nothing | **sources AND moving occluders fully dynamic** |
37
- | Reverb (reflection IR / per-band RT60) | yes, per **probe** | the SPACE's reverberation response at that point (room geometry + materials), source-agnostic | **sources fully dynamic** — any source routes into the reverb selected by the **listener's** position; geometry assumed mostly-static |
38
- | Pathing (corner-leakage) | yes | inter-probe **visibility graph** (the space's connectivity), source-agnostic | **sources fully dynamic** — the path source↔listener is solved at runtime from live positions; geometry static-assumed (+ live recheck below) |
39
-
40
- **Why reverb is source-agnostic** — "reverb = reflections with the source at the listener." The
41
- late/diffuse reverberant field of a room is approximately uniform and a property of the room (volume +
42
- surface absorption), not of where the source is. So one **listener-centric** reverb per probe serves
43
- every source in that room: `ProbeReverbRenderer` already selects by the *listener's* nearest probe, and
44
- all sources send into that one shared reverb bus. A source can move anywhere — it never indexes the bake.
45
-
46
- **Early reflections are the one source-position-dependent part.** The per-probe envelope bakes them
47
- listener-centric (correct when source ≈ listener; and when the source is far, the position-independent
48
- late tail dominates anyway). True per-source early reflections would be a separate real-time pass —
49
- deferred; the direct-path occlusion machinery already does per-source real-time work, and the late tail
50
- is the dominant reverb cue.
51
-
52
- **Moving objects (dynamic geometry):**
53
- - **Occlusion / transmission — fully live.** The occluder index is dynamic (O(1) `setTransform`), so a
54
- closing door / moving platform correctly blocks the *direct* path in real-time. No bake involved.
55
- - **Baked reverb + baked visibility — encode bake-time geometry.** A mover that changes acoustic
56
- *topology* (a door sealing a room) is not in the bake. Policy, in order of cost:
57
- 1. Most reverb-affecting geometry is static (walls/floors); small movers barely shift RT60 → accept.
58
- 2. **Pathing live recheck** (cheap, from the review): re-verify the chosen exit edge with a single
59
- `anyHit` ray each frame against live geometry — a closed door re-blocks the leak instantly even
60
- though the baked graph still has the edge. One ray per pathed source.
61
- 3. **Opt-in door variants** for big dynamic dividers (vault door, portcullis gating a whole area's
62
- reverb): bake open/closed variants for the affected probes and blend on door state (Steam Audio's
63
- "dynamic geometry" approach) — per set-piece, not the default.
64
- 4. **Re-bake affected probes** on major static-set changes (level streaming) — offline, rare.
65
-
66
- This is the standard **real-time-direct / baked-indirect** split (Steam Audio's exact model): live where
67
- source + geometry move, baked where it is expensive and mostly-static, with targeted live rechecks for
68
- the dynamic cases that matter.
69
-
70
- ---
71
-
72
- ## 0.5 Foundation data structures (owner-directed, resolved)
73
-
74
- These replace the v1 POJO/array structures across the existing + new probe code with the engine's
75
- binary-backed primitives.
76
-
77
- 1. **ProbeField store → `RowFirstTable`** (`core/collection/table/`), **not** `BinaryElementPool`. Probes
78
- are baked **write-once** and read by integer row index, so a fixed-schema columnar binary table fits
79
- exactly: one allocation, cache-friendly, no per-probe JS object. (`BinaryElementPool`'s alloc/free
80
- pool semantics are for churny dynamic elements — unneeded here.) Schema via `RowFirstTableSpec`:
81
- `x, y, z` (f32) · `reverbDecayLow/Mid/High` (f32, widened from the v1 scalar) · `bvhLeaf` (u32). The
82
- `AcousticProbe` class becomes a thin row cursor/accessor (or is dropped for row-index access).
83
-
84
- 2. **Pathing connectivity → `CSRGraph`** (`core/graph/csr/`), replacing `AcousticPathGraph.#adjacency`
85
- (array-of-arrays). Baked once ⇒ CSR is the ideal static-graph form — `edge_addresses` (row pointers) +
86
- `adjacency` + `edge_weights` in one buffer, with the cache-friendly `for (k=xadj[v]; k<xadj[v+1]; k++)`
87
- walk that `computeExitField`/Dijkstra want. `edge_weights` is **`Float32Array`** (owner-updated — its
88
- doc now cites "acoustic coupling"), so path distances go in **directly**, no quantization.
89
- `bakeVisibility` builds it two-pass (count degrees → prefix-sum `edge_addresses` → fill) or via
90
- `CSRGraph.fromArrays`.
91
-
92
- 3. **Probe nearest → BVH + `bvh_query_user_data_nearest_to_point`** (`core/bvh2/bvh3/query/`), replacing
93
- `ProbeField.nearest`'s linear scan. `ProbeField` builds a `BVH` of probe point-AABBs (leaf `user_data`
94
- = row index) once after baking (the `SpatialAudioIndex` precedent); `nearest(x,y,z)` = one query →
95
- row index, O(log n). This matters under the bake (`bakeVisibility` is O(probes²) nearest-style work)
96
- and per-frame (`computeExitField` snaps listener + every pathed source to a probe).
97
-
98
- 4. **`AcousticOccluderIndex` body → class** (replacing the `#bodies` POJO): a real `OccluderBody` holding
99
- `shape, material, matrix, inverse, localAABB, worldAABB` (the triangle `blas/indices/positions` of this
100
- v1 sketch are gone — §0.6 below raycasts the `shape` directly, so only meshes carry an internal BLAS).
101
- `matrix` (the **forward** world transform) maps the local hit position + normal back to world space.
102
-
103
- 5. **`body.inverse` mandatory — no `null` polymorphism.** `addBody`'s matrix defaults to `M4_IDENTITY`
104
- and the inverse is **always** computed; `#localizeRay` always transforms (drop the `inverse === null`
105
- fast-path). Uniform flow per [[feedback_uniform_flow]]. Production cost is nil — real occluder bodies
106
- always carry a `Transform` matrix anyway; the identity fast-path was a test-only convenience.
107
-
108
- **Landing:** (4)+(5)+the stored forward matrix = the foundation of **P1a/P1b**. (1)+(3) = part of the
109
- **P2** probe refactor (the probe schema widens for per-band decay there regardless). (2) = a pathing
110
- refactor that can ride with P2 or land standalone first.
111
-
112
- ---
113
-
114
- ## 0.6 Raycast shapes directly — drop the triangulation (owner-directed)
115
-
116
- The v1 `acoustic_geometry_from_shape` triangulates collider shapes (box → 12 tris, …) into mesh soup —
117
- counter-productive: lossy for primitives, more triangles, a per-body BLAS for a 2-tri wall. **Replace it
118
- with a first-class `AbstractShape3D.raycast`**, so the occluder index stores **shapes** and raycasts them
119
- exactly (closed-form for primitives, an internal BLAS only for meshes).
120
-
121
- **Contract:** `AbstractShape3D.raycast(hit: Float32Array, ray: Ray3) → boolean`, shape at its LOCAL
122
- origin (the caller localises the ray), `hit = [px, py, pz, nx, ny, nz]` (local surface point + unit
123
- normal). The hit distance `t` is recovered by the caller as `(hitPoint − origin)·dir` (unit dir).
124
-
125
- **It is mostly glue** — the narrowphase already factored the local math into core functions that
126
- `refine_ray_hit` dispatches to:
127
- - `SphereShape3D` → `core/geom/3d/sphere/sphere_raycast.js`
128
- - `BoxShape3D` → `core/geom/3d/box/box3_raycast.js`
129
- - `CapsuleShape3D` → `core/geom/3d/capsule/capsule_raycast.js`
130
- - `MeshShape3D` → an **internal lazy ebvh BLAS** (`ebvh_build_for_geometry_morton` +
131
- `ebvh_geometry_query_nearest_triangle_ray` + `construct_ray_hit_from_geometry`); the normal is
132
- normalised and flipped to face the incoming ray. (Owner-approved: MeshShape3D gains an internal BLAS.)
133
-
134
- `AcousticOccluderIndex` then holds `shape + material + matrix + inverse` per body; `closestHit`/`anyHit`
135
- localise the world ray into body space and call `shape.raycast`, transforming the hit point (by the
136
- forward matrix) and normal (by the rotation) back to world. **`acoustic_geometry_from_shape` is deleted**;
137
- `AcousticBody`/`AcousticSimulationSystem` pass the `Collider.shape` straight through. (Could later let
138
- the physics `refine_ray_concave` share `MeshShape3D.raycast` — out of scope.)
139
-
140
- ---
141
-
142
- ## 1. Model — acoustic ray-tracing energy histogram
143
-
144
- From a probe (source = listener), shoot N rays over the sphere. Each ray carries a **per-band energy
145
- throughput** `[low, mid, high]`, init `1`. Bounce loop (modeled on `PathTracer.path_trace`):
146
-
147
- 1. `index.closestHit` → `t`, world hit position + **normal** (P1a).
148
- 2. `pathLength += t`; deposit current energy into `histogram[band][bin]`, `bin = floor(pathLength / c / binSeconds)` (`c` = speed of sound).
149
- 3. fold absorption: `energy[band] *= (1 − material.absorption[band])`.
150
- 4. scatter: with prob `material.scattering` → diffuse (`getBiasedNormalSample`, cosine lobe about the normal); else specular (reflect direction about the normal).
151
- 5. `shiftForward(SELF_OCCLUSION_OFFSET · max(1,|origin|))`; Russian-roulette terminate (unbiased survivor scaling) after `min_bounce`.
152
- 6. miss (escaped to open sky) → stop.
153
-
154
- Per probe: the per-band histogram (the **reflectogram**) → **per-band RT60** (decay slope, via Schroeder
155
- integration) + the early bins (room signature). This is the classic CATT/ODEON statistical method, and
156
- the same energy model Steam Audio's baked reflections use.
157
-
158
- ---
159
-
160
- ## 2. Phases
161
-
162
- **P1 — Foundation (geometry-only tests).**
163
- - **✓ LANDED — P1a — `AbstractShape3D.raycast(hit, ray)`** (§0.6) on Sphere / Box / Capsule / Mesh — glue over the
164
- narrowphase `*_raycast` core functions + an internal mesh BLAS. Engine-wide, generally useful, purely
165
- additive to the shape classes. Test: known ray-vs-sphere/box/capsule/mesh hits (position + normal) and
166
- misses, in the shape's local frame.
167
- - **✓ LANDED — P1b — `OccluderBody` class + occluder-index refactor to shapes** (§0.5 (4)(5), §0.6). Bodies hold
168
- `shape + material + matrix + inverse` (inverse mandatory, identity default); `closestHit`/`anyHit`
169
- localise the ray + `shape.raycast`, transform hit point (forward matrix) + normal (rotation) to world,
170
- recover `t` for cross-body ordering; world-AABB broadphase from `shape.compute_bounding_box`. **Delete
171
- `acoustic_geometry_from_shape`**; `AcousticBody`/`AcousticSimulationSystem` pass `Collider.shape`
172
- through; `RayHit` gains world position + normal. Update the dependent specs (they currently feed
173
- triangle soup). Test: occlusion/ray behaviour unchanged on shape colliders; hit normal correct.
174
- - **✓ LANDED — P1c — scatter + offset primitives**: `v3_reflect` (core, new) for the specular bounce;
175
- the cosine-diffuse scatter reuses `getBiasedNormalSample` and the magnitude-scaled self-occlusion offset
176
- reuses `Ray3.shiftForward` directly (both already existed — no wrappers needed). Tested: reflection law
177
- (angle in = angle out, parallel-to-surface unchanged, unit-length preserved).
178
-
179
- **✓ LANDED — P2 — Bake core (no WebAudio).** `acoustic_trace_energy` (one ray's multi-bounce per-band
180
- deposit into a time-binned echogram — energy-threshold termination, not RR; see the deviation note up
181
- top) + `echogram_band_rt60` (Schroeder + ISO 3382 T30 slope) + `bakeReverbBands` (N rays → per-band
182
- RT60); `AcousticProbe` widened to `reverbDecay[3]` (scalar kept as the derived mean) + `ProbeField`
183
- table widened. **Sabine box gate passes** (baked cube RT60 ≈ 0.161·V/(S·α)). `bakeReverb` deleted.
184
- - `acoustic_trace_energy(index, origin, dir, …, histogram)` — one ray's multi-bounce energy deposit
185
- (allocation-free, seeded).
186
- - `ReflectionProbeRenderer.bake(pos, probe)` — N rays → per-band histogram → per-band RT60 + early
187
- envelope (mirrors `PathTracerProbeRenderer.bake`).
188
- - `AcousticReverbBaker.bake(probeField, ecd)` → `TaskGroup` (chunked over probes via `countTask`, like the
189
- LPV baker; logs ms/probe).
190
- - **Widen `AcousticProbe`**: `reverbDecaySeconds` scalar → `reverbDecay[3]` (per-band) + optionally a short
191
- early-reflection envelope; **keep the scalar derived from the bands** so the existing
192
- `reverbImpulseResponse` / `ProbeReverbRenderer` path stays valid as a regression gate until the new path
193
- is proven. Update `toJSON`/`fromJSON` + `bakeProbeField`.
194
- - **Validation gate — analytic Sabine box:** a closed box with known absorption has a known
195
- RT60 (`0.161·V/(S·α)`); assert the baked late-slope matches within tolerance. Plus: per-band absorption
196
- → per-band RT60 differs; open scene → ~no tail.
197
-
198
- **✓ LANDED — P3 — Render upgrade.**
199
- - `reverbImpulseResponse` shapes **per-band** decaying noise from `reverbDecay[3]` (each band's tail =
200
- its RT60), summed → a geometry-derived IR with room *timbre*. `ProbeReverbRenderer` consumes the
201
- widened probe. Test (`MockAudioContext`): IR band content reflects the per-band decays; crossfade still
202
- click-safe.
203
-
204
- **P4 — Optional, measure-first (not the keystone).**
205
- - NEE-to-source (deposit `source→wall→listener` paths for a cleaner early-reflection IR at lower ray
206
- count) — only if random-bounce variance is shown to be a problem.
207
- - **✓ LANDED — Body-level TLAS** over `AcousticOccluderIndex` — a dynamic BVH over the bodies' world AABBs,
208
- gather via `bvh_query_user_data_ray_segment` (leaf user-data = handle, `BvhClient` refit on
209
- `setTransform`). Landed early on owner request rather than waiting on the bake bench; `closestHit` /
210
- `anyHit` are now O(log n + hits).
211
-
212
- ---
213
-
214
- ## 3. Reuse map
215
-
216
- | Lifted **verbatim** (engine primitives) | Adapted / new (acoustic) |
217
- |---|---|
218
- | `getBiasedNormalSample` (cosine scatter), `construct_ray_hit_from_geometry` (hit normal), `random_point_on_sphere`, `Ray3.shiftForward` + scale-aware offset, the RR survivor-scaling pattern, `TaskGroup`/`countTask`, the baker ↔ probe-renderer split | the bounce loop (energy-time histogram vs radiance/SH), absorption-throughput fold, specular/diffuse split by `scattering`, Schroeder → RT60 reduction, per-band IR shaping |
219
-
220
- Keep the binary **`ebvh`** as the BLAS leaf (do not fork `BufferedGeometryBVH`'s inlined raycast). **Skip
221
- BVH8** (no CPU traversal kernel; lost to `ebvh` for 2-tri walls).
222
-
223
- ---
224
-
225
- ## 4. Key decisions
226
-
227
- - **Dx — source-agnostic space bake (see §0).** The bake = per-region reverb profile + connectivity;
228
- sources are fully runtime; reverb is selected by the *listener*; pathing is solved at runtime. Moving
229
- geometry is live for occlusion, static-assumed for baked indirect, with a live path-edge `anyHit`
230
- recheck + opt-in door variants. **No restriction to static sources, ever.**
231
- - **Statistical (energy-histogram) reverb**, one shared listener-centric IR per probe — affordable for
232
- WebAudio; not per-source convolution.
233
- - **Per-band throughout** (collapsing to a scalar is the regression we are fixing).
234
- - **Unbiased & leak-free**: RR survivor scaling is mandatory; the scale-aware self-occlusion offset is
235
- mandatory (a fixed epsilon silently leaks energy through walls at large world coords).
236
- - **Back-compat scalar path kept as the regression gate** until the per-band path validates against
237
- Sabine — *correctness-first, no bandaid*.
238
- - **Skip wetness/humidity** (owner-agreed) and the wet-JCA physics.
239
-
240
- ---
241
-
242
- ## 5. Testing
243
-
244
- Core (`core/` + `probe/`) is WebAudio-free → geometry-only tests, no audio mock. The Sabine box is the
245
- correctness gate for P2. The render (P3) uses `MockAudioContext`. Bench the bake ray cost before any
246
- TLAS. Run from the repo root: `npx jest --config jest.conf.json engine/sound/simulation`.
247
-
248
- ## 6. Risks
249
-
250
- - **Probe-contract widening** (scalar → per-band) ripples through `AcousticProbe` serialization,
251
- `bakeProbeField`, `reverbImpulseResponse`, `ProbeReverbRenderer` — hence P2 is the L item. Land P1
252
- first (both S, self-contained), keep the scalar path behind the new curve.
253
- - **Bake cost** (rays × bounces × probes) is real but offline; `TaskGroup` chunking keeps it
254
- non-blocking. Don't reach for the TLAS until the bench says so.
255
- - **Validation honesty**: trust the bake to replace `DECAY_PER_METRE` only after the Sabine-box RT60
256
- matches analytics.
257
-
258
- ## 7. Sequence
259
-
260
- P1a + P1b (S each, zero behavior change) → P2 (bake + Sabine gate) → P3 (render). P4 stays
261
- measured/optional.
@@ -1,121 +0,0 @@
1
- # Topologically-aware probe-visibility graph
2
-
3
- ## The cliff
4
-
5
- `bakeVisibility` connects every mutually-visible probe pair via an all-pairs `for i, for j<i` loop.
6
- With no occluders that is the **complete graph** — `n(n-1)/2` edges, O(n²) time *and* memory. A 128³ probe
7
- field (2,097,152 probes) would try to materialise **2.199e12 edges** → OOM. (Bench:
8
- `probe/bakeVisibility.bench.spec.js`.)
9
-
10
- Pruning a distance cutoff into that loop does **not** fix it — it still *enumerates* all pairs (O(n²) time),
11
- and the scheme is topologically blind. The fix is to never enumerate the complete graph: generate candidate
12
- edges from the **spatial topology** of the probe cloud, so we only ever touch O(n) of them.
13
-
14
- This is, in the literature, a **sparse geometric spanner** built like a **sparse roadmap spanner (SPARS2)**:
15
- a base graph + connectivity bridges + path-quality shortcuts. References that map 1:1 onto the three stages:
16
- SPARS/SPARS2 (Dobson & Bekris 2014 — names stage B a *"bridge"*, stage C a *"shortcut"*); the greedy
17
- geometric spanner (Bose et al. — stage C's length test `dist_G(u,v) > t·‖uv‖`); PRM* component-connection
18
- (stage B). Steam Audio, by contrast, keeps the all-pairs loop and just places **few** probes (5 m floor grid)
19
- — a placement lever we deliberately treat separately.
20
-
21
- ## Decisions (locked)
22
-
23
- - **Base = Delaunay, no jitter.** `compute_delaunay_tetrahedral_mesh` on the probe positions. (Grid fields
24
- have a clean dedicated tessellator, `tetrahedral_mesh_build_from_grid`, but wiring it by grid-structure is
25
- part of the *placement* workstream — not conflated here.)
26
- - **Full A + B + C** in this change.
27
- - **Dense probe fields stay.** This is purely a graph-construction fix; placement is a separate workstream.
28
- - Caveat we are buying with B+C: **3D Delaunay is not a proven constant-stretch spanner** (2D is, ≤1.998;
29
- 3D has no such bound — Erickson, Xia). So the base graph *can* detour badly and *can* be fragmented by
30
- walls; B repairs connectivity and C repairs path quality. They are mandatory, not optional.
31
-
32
- ## Stage A — Delaunay base graph (`probe_delaunay_edges.js`)
33
-
34
- 1. Copy probe positions into a packed `[x,y,z]*n`; `compute_delaunay_tetrahedral_mesh(mesh, points, n)`.
35
- 2. Extract the **unique undirected edges** of the live tets — 6 per tet (`getVertexIndex` pairs), deduped
36
- (materialise the freed-tet skip set once; do **not** call `exists()` per tet — it is O(free)). The tet
37
- mesh triangulates the convex hull, so this is a single connected graph of **≈6–7·n** edges.
38
- 3. Drop edges that fail `probe_edge_visible(field, index, a, b)`; keep survivors weighted by distance.
39
-
40
- Result: a sparse, occlusion-respecting base graph. **O(n)** edges, **O(n)** shadow rays. (2.1M probes →
41
- ~13M edges / rays vs 2.2e12 — ~150,000× fewer.) Dropping occluded edges is what fragments the graph into
42
- **islands** (rooms), which stage B repairs.
43
-
44
- ## Stage B — bridge the islands (`probe_visibility_bridge_components.js`)
45
-
46
- A door the Delaunay edge happened to miss leaves two acoustically-connected rooms as separate components.
47
-
48
- 1. `uf_init` + `uf_union` over the surviving base edges → connected components (canonical root = min member).
49
- 2. For each probe, **radius-query the probe BVH** (`bvh_query_user_data_intersects_sphere`, radius = k× the
50
- median base-edge length) for nearby probes in a **different** component; among the **visible** ones keep
51
- the **shortest** edge per unordered component-pair (the PRM/SPARS bridge heuristic).
52
- 3. Add those bridges, re-`uf_union`, repeat until the component count stops dropping. Genuinely sealed
53
- regions stay separate (correct — a closed vault should not leak).
54
-
55
- **O(n · |radius neighbours|)**, no all-pairs. Bridges are the only edges that can exceed the local Delaunay
56
- length, so they are also the place corner-leak pathing will most want.
57
-
58
- ## Stage C — augment for path quality (`probe_visibility_augment.js`)
59
-
60
- The Delaunay base routes only through local hops, so a graph path can detour or kink where a clear straight
61
- line exists. Add a **budgeted fraction** of extra edges (cap ≤ ~30% of base) on two criteria:
62
-
63
- - **C1 length (greedy spanner).** Candidate chords `(u,v)` from the 2-ring (a shared base neighbour `w`) and
64
- short BVH-radius pairs. Add iff `(u,v)` is visible **and** `dist_G(u,v) > t·‖uv‖` — the current graph route
65
- is ≥ t× the straight line. `dist_G` is evaluated with a **distance-bounded Dijkstra capped at `t·‖uv‖`**
66
- (reuse the heap Dijkstra; stop early once the cap is exceeded), so it is cheap, not a full SSSP per pair.
67
- - **C2 directionality (diffraction-angle gate).** For a 2-ring kink `u–w–v`, with `d0 = ̂(u−w)`, `d1 = ̂(v−w)`,
68
- add the chord iff visible **and** `dot(d0, −d1) < X` — the angle at `w` is acute, a sharp corner the sound
69
- must wrap around. Acoustically a bend is a diffraction point whose HF loss grows with angle (UTD/Maekawa),
70
- so straightening it is worth an edge even when it barely shortens the route. This is an acoustic heuristic,
71
- **not** a spanner property — hence the budget cap, so it composes with C1 rather than overrunning it.
72
-
73
- **O(n · degree²)** candidates from the 2-ring; each gated test is local/bounded. Process shortest-first,
74
- stop at the budget.
75
-
76
- ## Assembly — `bakeVisibility` rewrite
77
-
78
- `A (delaunay edges → visible) → B (bridge) → C (augment) → CSRGraph (both half-edges) → field.setVisibilityGraph`.
79
- The CSR build, the `EDGE_WEIGHT_BLOCKED` runtime recheck, and `findAcousticPath` are unchanged downstream —
80
- this only changes which edges exist, not their representation. Weights remain inter-probe distance so the
81
- recheck restores them identically.
82
-
83
- ## New primitives that may be needed
84
-
85
- - Tet → unique-edge extraction with an O(tets) freed-skip (helper in `probe_delaunay_edges.js`).
86
- - A median base-edge-length helper to set the stage-B radius and the stage-C candidate radius.
87
- - (If 2-ring proves insufficient at scale) WSPD candidate pairs — deferred unless measured to be needed.
88
-
89
- ## Test plan
90
-
91
- - `probe_delaunay_edges.spec.js`: a small grid tetrahedralises to the expected adjacency; coincident/degenerate
92
- inputs; edge uniqueness.
93
- - `bakeVisibility.spec.js` (extend): empty scene is now **sparse** (≈O(n), assert ≪ n²) yet still **connected**
94
- (one component) and every kept edge is visible; a wall fragments then B re-bridges through a gap; the
95
- existing two-rooms-corridor integration test still passes (reachability + direction + per-band).
96
- - Re-point `bakeVisibility.bench.spec.js`: the complete-graph assertion becomes a **sparsity + connectivity**
97
- assertion; the 128³ case should now *complete* (sweep added to confirm ~linear edges/time).
98
- - Stage C: a deliberate kink (L-shaped corridor with a clear diagonal) gains the shortcut; budget cap honoured.
99
-
100
- ## Open knobs (defaults to pick during implementation, document in code)
101
-
102
- - stage-B radius multiplier `k` (× median edge length); stage-C stretch `t`; stage-C angle `X`; stage-C budget
103
- fraction. All bake-time constants with JSDoc rationale (per the project's no-magic / uniform-flow style).
104
-
105
- ## As-built notes (landed)
106
-
107
- - **Defaults chosen:** stage-B radius `2.5×` median edge length; stage-C stretch `1.3`, kink `dot < −0.5`
108
- (~corners <60°), budget `0.2×` base edges.
109
- - **Coplanar degeneracy is handled, not avoided:** a valid 3D Delaunay 1-skeleton is connected, so
110
- `probe_delaunay_edges` checks connectivity and falls back to the complete graph when the extraction is
111
- disconnected (a near-coplanar/degenerate field — e.g. the y=0 test scenes). Dense 3D fields never hit it.
112
- - **Stage B is boundary-driven:** it searches only from the endpoints of dropped (occluded) edges (`sources`),
113
- not all `n` probes — an interior probe with all-visible neighbours can't be a missed-door near side. An
114
- empty scene drops nothing, so stage B is a no-op (measured: 10 s → 20 ms at 13.8k probes).
115
- - **Spatial sort:** `probe_delaunay_edges` Morton-sorts the points before the incremental tetrahedralisation
116
- (the core's own TODO) and maps tet vertices back through the permutation. ~25% off the build.
117
- - **Result:** edges are O(n) (measured 6.7–8.0 per probe across 8³–32³, always 1 component); the old all-pairs
118
- O(n²)/OOM is gone. The 128³ case no longer OOMs and completes.
119
- - **Remaining bottleneck = the core incremental Delaunay BUILD** (~98% of bake time, super-linear). Follow-ups
120
- (out of scope here): wire `tetrahedral_mesh_build_from_grid` for grid fields (O(n) — the placement
121
- workstream), and/or speed up core's Delaunay (BRIO/spatial-sort insertion is its standing TODO).