@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,419 +0,0 @@
1
- # Codec layout — verification before the move
2
-
3
- 2026-09-02. Re-derivation of the earlier session's claims about where the file-format codecs sit and
4
- what they depend on, done by resolving imports rather than by reading directory listings. Every
5
- claim is marked **confirmed**, **corrected** or **refuted**, with the command that decided it.
6
-
7
- All commands run from `packages/meep-engine/src`. The suite was green before anything moved:
8
- `npx vitest run` — 2227 files, 15393 tests, 69 s.
9
-
10
- The one tool this used that the earlier session did not is an import resolver: the task's grep
11
- (`from ['"].*(engine|shade|view)/`) only matches specifiers that *spell* one of those directory
12
- names, so a file inside `shade/renderer/loader/gltf/` importing `../../texture/TextureRole.js` is
13
- invisible to it. Resolving each relative specifier against the importing file and classifying the
14
- result by its top-level directory catches those. The script is a few lines of node; the resolved
15
- tables below come from it.
16
-
17
- ```bash
18
- # resolve every relative import under DIR and print those landing in engine/, shade/ or view/
19
- node resolve_imports.mjs avif basis ktx2 shade/renderer/loader/gltf shade/renderer/loader/usd \
20
- engine/asset/loaders/image/png engine/asset/loaders/image/jpeg
21
- ```
22
-
23
- ---
24
-
25
- ## Claim A — pure core plus thin skin: **confirmed, with three corrections and one finding that changes the rule**
26
-
27
- Resolved imports into `engine/`, `shade/` or `view/`, production files only:
28
-
29
- | Tree | Files importing engine/shade/view | What they import |
30
- |---|---|---|
31
- | `avif/native/` (110 files) | **1** — `api/avif_to_sampler2d.js` | `engine/graphics/texture/sampler/Sampler2D.js` |
32
- | `avif/` top level (3 files) | 0 | the encoder pool depends on `native/` and on `OffscreenCanvas` |
33
- | `basis/` (26 files) | **2** — `transcode_basis.js`, `transcode_uastc.js` | `TextureFormat`, `ShadeTexelData`, `texture_mip_size` |
34
- | `ktx2/` (7 files) | **2** — `ktx2_read.js`, `vk_format_to_texture_format.js` | `ShadeTexelData`, `gpu_texture_format_is_block_compressed`, `TextureFormat` |
35
- | `loaders/image/png/` | **1** — `prototypePNG.js` | `Sampler2D`, `sampler2d_to_html_canvas`, `AssetRequestScope`, `ArrayBufferLoader` |
36
- | `loaders/image/jpeg/` | 0 | — |
37
- | `loader/gltf/` | see below | — |
38
- | `loader/usd/` | see below | — |
39
-
40
- **Correction A1 — the AVIF skin is one file, not two.** `sampler2d_to_avif.js` imports nothing from
41
- `engine/`; it takes a duck-typed `{data, width, height, itemSize}` and reaches only `core/`. It is an
42
- adapter by *intent* (it is named after, and is the inverse of, the `Sampler2D` reader) but not by
43
- dependency. It still moves with its inverse, because the two are documented as a pair (AVIF decision
44
- D32) and their round-trip spec tests them together — but the reason is cohesion, not the rule.
45
-
46
- **Correction A2 — PNG has two doors, not one.** `ImageDecoderWorker.js` imports `png/PNGReader.js`
47
- and `codec/ThreadedImageDecoder.js` imports `png/PNG_HEADER_BYTES.js`. Both sit in
48
- `engine/asset/loaders/image/`, so the earlier "the worker is the only door" undercounted by one; the
49
- practical consequence is the same — two import lines to update. `prototypePNG.js` is a browser dev
50
- harness (hard-coded asset path, appends a canvas to `document.body`) and imports the engine; it is
51
- not format code and cannot move into `format/`. It moves up one level to `loaders/image/`.
52
-
53
- **Correction A3 — the glTF/USD seam is not where the earlier table drew it.** The resolver, not the
54
- grep, decides:
55
-
56
- ```
57
- glTF, engine-free (imports core/ only, or nothing):
58
- gltf_parse_container.js gltf_read_accessor.js gltf_component_type.js GLTF_PRIMITIVE_MODE.js
59
- gltf_used_image_indices.js coalesce_array_duplicates.js gltf_node_world_matrices.js ext/**
60
- glTF, engine-facing:
61
- load_gltf.js tiny-gltf.js gltf_create_material.js gltf_create_texture.js
62
- fix_up_material_sides.js gltf_parse_alpha_mode.js (TransparencyMode) gltf_image_roles.js (TextureRole)
63
-
64
- USD, engine-free:
65
- parse_usda.js usd_triangulate.js usd_lz4.js unpack_usdz.js UsdError.js usd_compose_transform.js
66
- USD, engine-facing:
67
- load_usd.js usd_build_scene.js usd_decode_image.js (ShadeTexture, ShadeImage)
68
- USD, import-free but renderer-facing by contract:
69
- UsdExtensionRegistry.js — its hooks return Node3D; it exists to plug into usd_build_scene
70
- ```
71
-
72
- Against the earlier table: `gltf_node_world_matrices` (pure, and already consumed by the node-side
73
- virtual-geometry builder) was missing from the core list; `usd_compose_transform` was listed as skin
74
- and is pure; `UsdExtensionRegistry` was listed as core and is the scene builder's plugin registry.
75
- `gltf_image_roles` and `gltf_parse_alpha_mode` are skin, which the task's grep could not see.
76
-
77
- **Is `ktx2_read.js` a clean extraction or a rewrite? Extraction.** The engine coupling is in exactly
78
- three places, all at the boundary:
79
-
80
- 1. The three `return` sites construct `ShadeTexelData` (two of them via the transcoders).
81
- 2. Level byte-length validation is *delegated* to that constructor — the reader deliberately does
82
- not check that a level's byte count matches its format and extent ("one authority on what a level
83
- should weigh"). Two spec tests depend on this delegation.
84
- 3. Its refusals are phrased in terms of what `ShadeTexelData` can model (no arrays, no cubes, no 1D).
85
-
86
- The decode path itself — header, DFD, key/values, level index, supercompression, Basis dispatch —
87
- touches nothing of the engine. The cut is: return a plain `{format, width, height, depth, levels}`
88
- record; have the reader check each level's byte length against the format table itself (it is a
89
- container reader, and a mislabelled payload is a malformed file); and move the *engine's*
90
- constraint — "one level or the whole chain" — into the adapter that builds `ShadeTexelData`, where
91
- the two spec tests that exercise it go too. The refusal messages that name `ShadeTexelData` are
92
- reworded to name the constraint. That is a dozen lines and two relocated tests, not a redesign.
93
-
94
- **Finding A4 — the texture tier speaks WebGPU format strings, and rule 1 as written cannot be met
95
- literally without duplication.** Every file on the texture side — the transcoders' target tables,
96
- `vk_format_to_texture_format`, the reader's block-compressed refusals — is written in terms of
97
- `TextureFormat` (the `GPUTextureFormat` enum as named constants) and `gpu_texture_format_info` (the
98
- block-size table), both under `shade/descriptor/texture/format/`, plus `texture_mip_size` from
99
- `shade/util/`. These are not engine code: they are a web standard's vocabulary as data, with no
100
- device, no renderer and no DOM behind them, and `shade/descriptor/` as a whole imports nothing but
101
- `core/` and its own `util/`. A transcoder that writes BC7 blocks has to be able to say "BC7", and the
102
- alternatives — a parallel enum inside `format/` translated at the adapter, or moving three vendored
103
- Shade files into `core/` — are either duplication or a change to Shade's layout that this refactor has
104
- no business making. Importer counts (5, 12 and 11 respectively) are small enough that moving them
105
- would be *possible*; it would just be the wrong reason to do it.
106
-
107
- So rule 1 is refined: **a format package may import `core/`, npm packages, and an explicit allowlist
108
- of three vocabulary modules — and nothing else under `shade/`, `engine/` or `view/`.** The allowlist
109
- is enumerated in the structural spec so it is a finite, visible list rather than a directory
110
- exception, and any fourth entry has to be argued for in a diff. This is the same shape as
111
- `STILL_COUPLED` in `meep_three_free.spec.js`, and it stays greppable.
112
-
113
- **Revised 2026-09-02.** The allowlist is a statement about depth, not about directories. A format
114
- module may import anything that is itself as low-level as `core/` — the structural spec's second test
115
- holds every entry to importing nothing but `core/` and other entries — and `Sampler2D` qualifies, a
116
- typed array with a width and a height. It joined the list together with the two error measures under
117
- `engine/graphics/texture/sampler/compare/` when the transcode and AVIF specs' five private PSNR copies
118
- were consolidated there. The rule's job is unchanged: no device, renderer or DOM behind a codec.
119
-
120
- ---
121
-
122
- ## Claim B — JPEG is dead: **confirmed**
123
-
124
- ```bash
125
- grep -rn "jpeg_decode\|JpegImage\|buildHuffmanTable\|idct8x8\|JpegFrame\|decodeScan\|buildComponentData\|jpeg_component_row" \
126
- --include=*.js . | grep -v "^./engine/asset/loaders/image/jpeg/"
127
- # → empty
128
- grep -rn "loaders/image/jpeg\|/jpeg/" --include=*.js --include=*.json . \
129
- | grep -v "^./engine/asset/loaders/image/jpeg/" | grep -v "avif/native/av1/tables"
130
- # → empty
131
- ```
132
-
133
- `guessAssetType.js` maps `jpg`/`jpeg`/`png` to `"image"`. The `"image"` loader is
134
- `ImageRGBADataLoader`, whose codec chain is `CodecWithFallback(ThreadedImageDecoder, NativeImageDecoder)`.
135
- `ThreadedImageDecoder.test` sniffs `PNG_HEADER_BYTES` and returns false for everything else;
136
- `NativeImageDecoder` inherits `Codec.test`, which returns true unconditionally, and decodes through
137
- `new Image()` and a canvas. So every JPEG the engine has ever loaded went through the browser's
138
- decoder. The in-tree decoder is reachable from nothing but its own three spec files, which pass.
139
-
140
- ---
141
-
142
- ## Claim C — `engine/asset/loaders/gltf/` is dead: **already resolved — the directory does not exist**
143
-
144
- ```bash
145
- ls engine/asset/loaders/gltf # → No such file or directory
146
- git log --oneline -3 -- engine/asset/loaders/gltf
147
- # a10895194 vestigial GLTF code from before Shade
148
- ```
149
-
150
- Commit `a10895194` (five commits back) removed it. Migration step 1 is a no-op and is dropped.
151
-
152
- ---
153
-
154
- ## Claim D — `core/binary/` is the right home for generic compression: **confirmed; the stated reasoning was wrong**
155
-
156
- The claim's justification — "zstd is used by KTX2 and by USD via lz4" — conflates two codecs. The
157
- actual consumers:
158
-
159
- ```bash
160
- grep -rln "binary/lz4\|binary/zstd" --include=*.js . | grep -v "^./core/binary/" | grep -v spec
161
- # ktx2/ktx2_read.js (zstd)
162
- # shade/renderer/geometry/virtual/format/frame/vgeo_*.js (zstd, three files)
163
- # shade/renderer/loader/usd/usd_lz4.js (lz4)
164
- grep -rn "binary/meshopt" --include=*.js . | grep -v "^./core/binary/meshopt/" | grep -v spec
165
- # format/scene/gltf/ext/meshopt/MeshoptBufferViewExtension.js (three imports)
166
- ```
167
-
168
- zstd is shared between KTX2 and the virtual-geometry frame format; lz4 is USD-only today; meshopt is
169
- glTF-only today. So "shared between formats" holds for zstd alone. What actually justifies the tier
170
- is not the consumer count but the *dependency*: `core/binary/meshopt/` imports `core/assert`,
171
- `core/math` and `core/geom` and nothing else — it is a byte-array-in, byte-array-out codec with no
172
- notion of a bufferView, an accessor or a `KHR_` prefix. Everything glTF-specific about meshopt
173
- (reading the extension object, validating stride and count rules per mode, the two extension names'
174
- filter sets and version ceilings) already lives in `gltf/ext/meshopt/MeshoptBufferViewExtension.js`,
175
- exactly where the plan document that introduced it (`MESHOPT_COMPRESSION_PLAN.md` §1) drew the
176
- seam. `MeshoptCompressionFilter`/`Mode` cite the glTF spec in their doc comments because that is where
177
- the numbering is defined, not because they depend on it.
178
-
179
- **Held: the tier boundary is dependency direction, and meshopt is below formats by that test.**
180
- That it happens to have one consumer is a fact about today's format list, not about the codec.
181
-
182
- ---
183
-
184
- ## Claim E — consumer counts are small: **confirmed for five of seven; glTF is larger and USD is zero**
185
-
186
- Production importers from outside each tree (specs listed separately):
187
-
188
- | Tree | Outside importers (prod) | Files | Outside spec importers |
189
- |---|---|---|---|
190
- | `avif/` | 3 lines | `shade/renderer/scene/serialization/{decode_image_source_hdr,deserialize_scene,write_image_source}.js` | 0 |
191
- | `basis/` | 2 files | `ktx2/ktx2_read.js`, `shade/renderer/loader/gltf/tiny-gltf.js` | 0 imports; **1 spec reading `basis/__fixtures__` by filesystem path** (`gltf_texture_basisu.spec.js`) |
192
- | `ktx2/` | 1 file | `shade/renderer/loader/gltf/tiny-gltf.js` | 2 (`basis/transcode_*.spec.js` read their fixtures through `ktx2_read`) |
193
- | `png/` | 2 files, both inside `loaders/image/` | `ImageDecoderWorker.js`, `codec/ThreadedImageDecoder.js` | 0 |
194
- | `jpeg/` | 0 | — | 0 |
195
- | `loader/gltf/` engine-free files | **5 files** | `shade/renderer/geometry/virtual/build/gltf/{gltf_open_document,gltf_collect_geometries,gltf_read_geometry}.js` + `GltfDocument.spec.js` + `engine/asset/loaders/gltf_meshopt_compression_loading.spec.js` | — |
196
- | `loader/gltf/` engine-facing (`load_gltf`) | 5 files | `engine/asset/loaders/GLTFSceneBundleAssetLoader.js`, four `shade/playground/*/main.js` | 1 |
197
- | `loader/usd/` | **0** | nothing imports `load_usd` anywhere in the repository | 0 |
198
-
199
- Outside this package: the game repository at `H:/git/moh` vendors a frozen *copy* of meep
200
- (`app/src/mir-engine/meep`, version 3.1.0) and its own code imports no codec path
201
- (`grep -rn "src/(avif|basis|ktx2)/|loaders/image/(png|jpeg)/|renderer/loader/(gltf|usd)/"` over
202
- `moh` excluding the vendored copy and coverage output → empty). `packages/gpu-inspector-tool` imports
203
- only `shade/device/timing/profile/*`. **No external importer breaks.**
204
-
205
- The migration order changes on one count: the glTF split touches the virtual-geometry builder, which
206
- the earlier plan did not mention, so it stays last — but for blast radius, not for "judgement about
207
- the seam" (Claim A settled the seam). USD having zero importers means it is a public API with no
208
- in-tree caller; that is not a reason to delete it (the package exports `./src/*`), but it is worth
209
- knowing that nothing but its own specs will exercise the move.
210
-
211
- ---
212
-
213
- ## Hazards — the earlier list, checked, plus what it missed
214
-
215
- | Hazard | Status |
216
- |---|---|
217
- | `threaded_image_encoder.js:122` resolves `./encoder_worker.js` via `import.meta.url` | **Real.** The three encoder files move together. It is the only worker `new URL` outside `engine/physics/fluid` (`grep -rn "new URL(" --include=*.js`). |
218
- | `__fixtures__/` relative paths | Fixture loaders resolve relative to their own spec (`path.resolve(HERE, "__fixtures__")`) and move with them. `basis/__fixtures__/index.json` holds no paths. |
219
- | `rollup.config.js` | Bundles `engine/asset/loaders/image/ImageDecoderWorker.js`, which stays put; only its import of `PNGReader` changes. `tsconfig.types.json` globs `src/**`. `vitest.config.mjs` globs. `package.json#files` excludes `src/**/__fixtures__/**` by glob. |
220
- | specs beside sources | Moved with them. |
221
- | `ktx2` → `basis` edge | Legal within `format/texture/`. |
222
- | **Missed:** `shade/renderer/loader/gltf/gltf_texture_basisu.spec.js:24,196` reads `../../../../basis/__fixtures__/*.ktx2` by filesystem path | Breaks silently as a missing file, not an import error. Updated in step 2. |
223
- | **Missed:** `jsdoc_function_type_contract.spec.js` skips top-level `VENDORED = ["shade", "avif"]` | After the move `avif` is no longer top-level; the skip becomes dead. The avif tree contains zero instances of the anti-pattern (`grep` for `function(name:` inside braces → 0), so it can simply be scanned. Entry removed. |
224
- | **Missed:** `SHADE_ORIGIN.md` records `src/avif` "kept as a sibling so Shade's `../../../../avif/…` imports still resolve" | A note is added under the divergences. |
225
- | **Missed:** documentation links | `IMAGE_CODEC_REVIEW_2026_08_28.md` (~70 relative links into `avif/native`, `png/`, `jpeg/`), `COMPRESSED_TEXTURE_PLAN_2026_09_01.md` (8), `MESHOPT_COMPRESSION_PLAN.md`, `tools/geometry/virtual/README.md`, `core/color/COLOR_REVIEW_2026_08_28.md`, `avif/native/DECISIONS.md`. Rewritten with `sed` per step. `REVIEW_2026_08_06/*.md` cite absolute paths on another machine and are left as the snapshots they are. |
226
- | **Missed:** `usd_decode_image.js` carries a *third* PNG decoder (8-bit RGB/RGBA, fixed-Huffman only) | Not a layout matter; recorded as a follow-up below. |
227
-
228
- ---
229
-
230
- ## The principle, as verified
231
-
232
- Unchanged in substance: sort by dependency direction. `core/binary/` below `format/`, `format/` below
233
- adapters, adapters beside the engine type they produce, loaders on top. Two rules:
234
-
235
- 1. **A format package imports `core/`, npm packages, and the vocabulary allowlist —
236
- nothing else under `engine/`, `shade/` or `view/`.** Enforced by
237
- `src/format_tier_boundary.spec.js`, written in the style of `meep_three_free.spec.js`, added in
238
- step 2 so every later step runs under it rather than after it.
239
- 2. **An adapter lives beside the engine type it produces**, not beside the format it reads.
240
-
241
- ## The tree, as it will be built
242
-
243
- The `image/texture/scene` grouping is kept. The task's test for it was whether it predicts the
244
- adapter, and it does at the level that matters — what kind of thing comes out:
245
-
246
- | Group | Decodes to | Adapter target(s) actually found |
247
- |---|---|---|
248
- | `image/` | interleaved pixel samples | `Sampler2D` (AVIF, directly; PNG via `ImageRGBADataAsset.create()`, which returns a `Sampler2D`) |
249
- | `texture/` | GPU-format block payloads | `ShadeTexelData` (KTX2; Basis reaches it only through KTX2) |
250
- | `scene/` | node graphs | `SceneBundle` (glTF), `Node3D[]` (USD — no bundle; `load_usd` returns roots) |
251
-
252
- Two of seven are loose at the *type* level (PNG's asset wrapper, USD's bare array) and none at the
253
- *kind* level. That is enough to justify one directory level; a flat seven would lose the fact that
254
- `ktx2 → basis` is a within-kind edge. Recorded so the next person does not re-derive it.
255
-
256
- ```
257
- src/format/
258
- image/avif/ ← avif/native/** (api/ loses the two sampler files; index.js loses their exports)
259
- image/png/ ← engine/asset/loaders/image/png/** minus prototypePNG.js
260
- image/jpeg/ ← engine/asset/loaders/image/jpeg/**
261
- texture/basis/ ← basis/**
262
- texture/ktx2/ ← ktx2/**
263
- scene/gltf/ ← the engine-free glTF files listed under A3, plus ext/**, plus MESHOPT_COMPRESSION_PLAN.md
264
- scene/usd/ ← the engine-free USD files listed under A3
265
-
266
- src/shade/renderer/texture/source/texel_data_from_ktx2.js NEW — the ShadeTexelData half of ktx2_read
267
- src/engine/graphics/texture/sampler/avif_to_sampler2d.js ← avif/native/api/ (names kept, see below)
268
- src/engine/graphics/texture/sampler/sampler2d_to_avif.js ← avif/native/api/
269
- src/engine/asset/loaders/image/avif/{threaded_image_encoder,encoder_worker,encode_image_source}.js
270
- src/engine/asset/loaders/image/prototypePNG.js ← png/prototypePNG.js
271
- src/engine/asset/loaders/image/codec/JpegCodec.js NEW — decision 1
272
- ```
273
-
274
- Deviations from the proposed tree, and why:
275
-
276
- - **No `texel_data_from_basis.js`.** Nothing outside `ktx2_read` calls the transcoders with a
277
- payload; `tiny-gltf` imports only their `*_formats()` lists. One adapter, one door.
278
- - **No `vk_format_to_texture_format.js` in `shade/renderer/texture/source/`.** It maps Vulkan format
279
- numbers to WebGPU format strings using nothing but the vocabulary allowlist. It is KTX2 knowledge and
280
- stays in `format/texture/ktx2/`. Moving it would have been the earlier session's rule 1 forcing a file
281
- uphill for want of the refinement in A4.
282
- - **The AVIF sampler pair keeps its file and function names** (`avif_to_sampler2d`,
283
- `sampler2d_to_avif`) rather than becoming `sampler2d_from_avif`. Three consumers, a decision log
284
- (D32) and a plan document name them; the destination directory is not uniformly prefixed
285
- (`downloadSamplerAsPNG.js`, `HarmonicDiffusionGrid.js`); and a rename is not what this refactor is for.
286
- - **`avif/index.js` is deleted, `avif/native/index.js` becomes `format/image/avif/index.js`.**
287
- Neither has an importer. The outer one re-exported the worker pool alongside the codec, which is
288
- the tier mix this refactor exists to undo; its `decode`/`encode` aliases for the old WebAssembly
289
- names have no caller.
290
- - **`UsdExtensionRegistry.js` stays with `usd_build_scene`.** Import-free, but its whole contract is
291
- "return a `Node3D`", and it is consulted from the builder. Dependency direction is the rule; where
292
- a file has none, the type its contract names decides.
293
- - **The encoder pool lands in `engine/asset/loaders/image/avif/` with a caveat.** It encodes, and
294
- "loaders" is the wrong verb. It goes there anyway because its decode-direction sibling
295
- (`codec/ThreadedImageDecoder.js`, also a worker pool) already lives one directory over, and one
296
- place for "image codec worker pools" beats a new top-level directory for three files.
297
-
298
- ## Decision 1 — JPEG: **move it and wire it, as the last fallback**
299
-
300
- The 2026-08-28 review (item C1) recommended registering it behind `Codec`, and its item C4 was then
301
- *done* — `getData` was rewritten around `jpeg_component_row.js` a week ago, with byte-for-byte
302
- verification against the old output. Deleting a decoder somebody just finished refactoring, with
303
- three passing spec files, would be the wrong reading of "dead". It is unreachable, not unwanted.
304
-
305
- What "wire it" means here is constrained by one fact: in a browser, `NativeImageDecoder` decodes
306
- JPEG with the platform's own decoder, and a pure-JavaScript decoder on the main thread is slower and
307
- blocks. So `JpegCodec` goes **after** `NativeImageDecoder` in `ImageRGBADataLoader`'s chain —
308
- `[ThreadedImageDecoder, NativeImageDecoder, JpegCodec]`. `NativeImageDecoder.test` claims everything
309
- (it inherits the base class's `return true`), so in a browser nothing changes; where there is no
310
- `Image` — node, a worker — the native decoder throws, `CodecWithFallback` moves on, and JPEG becomes
311
- loadable for the first time. Zero behaviour change where it already worked; a working decoder where
312
- it did not. Teaching `ImageDecoderWorker` to decode JPEG off the main thread is the version with a
313
- real payoff for the browser, and it is a measured trade (a JS decoder in a worker versus the
314
- platform's on the main thread), so it is a follow-up rather than a side effect of a layout change.
315
-
316
- ## Decision 2 — how far the skins are cut
317
-
318
- | Skin | Verdict | What is done |
319
- |---|---|---|
320
- | `ktx2_read.js` | extraction | plain record out; per-level byte check moves in; "1 or full chain" moves to the adapter with its two tests |
321
- | `transcode_basis.js`, `transcode_uastc.js` | extraction | plain record out; `TextureFormat` stays via allowlist |
322
- | `avif_to_sampler2d.js` | already separate | relocated |
323
- | glTF, USD | file moves only | the seam is a file boundary already |
324
- | `tiny-gltf.js` | left as is | it *is* the loader; it imports the adapter |
325
- | `usd_decode_image.js` | left as is, then taken as a follow-up | duplicate PNG decoder, since replaced by `PNGReader` + the adapter |
326
- | `UsdExtensionRegistry.js` | left with the builder | see above |
327
-
328
- ## Migration order, resequenced
329
-
330
- Each step: `git mv`, fix imports, run the suite, commit with `--only`.
331
-
332
- 0. This document.
333
- 1. ~~Delete `engine/asset/loaders/gltf/`~~ — already gone (`a10895194`).
334
- 2. `format/texture/` — Basis and KTX2; the `ShadeTexelData` cut; the adapter; the structural spec;
335
- `gltf_texture_basisu.spec.js` fixture path; `tiny-gltf` import.
336
- 3. `format/image/avif/` — plus the sampler pair to `engine/graphics/texture/sampler/`, the encoder
337
- trio to `engine/asset/loaders/image/avif/`, the jsdoc spec's vendored list, `SHADE_ORIGIN.md`.
338
- 4. `format/image/{png,jpeg}/` — plus `prototypePNG.js` up a level, `JpegCodec`, the two PNG doors.
339
- 5. `format/scene/{gltf,usd}/` — plus the virtual-geometry builder's five imports and the tools README.
340
-
341
- Step 6 (the structural spec) is folded into step 2 so it guards steps 3–5 as they land.
342
-
343
- ## As executed
344
-
345
- Six commits on `master`, each green on the full suite (2227 → 2230 spec files; 15393 → 15401 tests —
346
- the adapter spec, the JPEG codec spec, the loader chain tests and the boundary spec are the additions).
347
- The plan above held. What execution added:
348
-
349
- - **Moves went file by file, not by directory.** A directory rename under `src/` was refused with
350
- "Permission denied": two vite dev servers and an IDE hold watcher handles on the tree, and Windows
351
- will not rename a directory with an open handle. Moving the files out of it is not blocked, so the
352
- move tool does that and removes the emptied directories afterwards. History follows the files
353
- either way (`git log --follow`).
354
- - **Imports and markdown links were rewritten by resolution, not substitution.** Every relative
355
- specifier — `import … from`, `export … from`, `import()`, `new URL(…, import.meta.url)`, and
356
- `](…)` links with `#L` anchors or `:line` suffixes — was resolved against the file's old location,
357
- mapped through the move, and re-expressed from the new one. Only specifiers whose target or whose
358
- own file moved were touched. Paths in *prose* (backticked, not linked) were updated by hand where
359
- they name a live file; recipes of the form `git checkout <sha> -- src/avif/native/…` were left, since
360
- they name a path at that commit.
361
- - **Some working-copy files are CRLF** (`core.autocrlf=true`; `jsdoc_function_type_contract.spec.js`
362
- was one). Edit scripts that match multi-line blocks have to match `\r?\n` and write the file's own
363
- ending back.
364
- - **The rollup worker bundle was built** after step 4 (`npx rollup -c`) to prove
365
- `ImageDecoderWorker.js` still resolves `PNGReader` from `format/image/png/`; it does, and the output
366
- was removed again (`build/` is gitignored).
367
- - **`Base64.decode` returns an `ArrayBuffer`**, which the codec `test()` contracts do not accept;
368
- the two new JPEG specs wrap it in a `Uint8Array`.
369
-
370
- Final shape, as committed:
371
-
372
- ```
373
- src/format/
374
- image/avif/ api/ av1/ bitstream/ color/ heif/ isobmff/ __fixtures__/ index.js DECISIONS.md NOTICE.md
375
- image/png/ PNG.js PNGReader.js PNG_HEADER_BYTES.js chunk/ filter/ crc32.js png_inflate.js png_unpack_samples.js
376
- image/jpeg/ JpegImage.js jpeg_decode.js jpeg_component_row.js … NOTES.md
377
- texture/ TexelPayload.js
378
- texture/basis/ etc1s/ uastc/ transcode/ BasisBitReader.js BasisHuffmanTable.js transcode_basis.js transcode_uastc.js __fixtures__/
379
- texture/ktx2/ ktx2_read.js ktx2_read_basis_lz_global_data.js vk_format_to_texture_format.js KhrDfModel.js SupercompressionScheme.js __fixtures__/
380
- scene/gltf/ gltf_parse_container.js gltf_read_accessor.js gltf_component_type.js GLTF_PRIMITIVE_MODE.js
381
- gltf_used_image_indices.js coalesce_array_duplicates.js gltf_node_world_matrices.js ext/ MESHOPT_COMPRESSION_PLAN.md
382
- scene/usd/ parse_usda.js usd_triangulate.js usd_lz4.js unpack_usdz.js UsdError.js usd_compose_transform.js
383
-
384
- src/shade/renderer/texture/source/texel_data_from_ktx2.js KTX2 → ShadeTexelData
385
- src/engine/graphics/texture/sampler/{avif_to_sampler2d,sampler2d_to_avif}.js AVIF ↔ Sampler2D
386
- src/engine/asset/loaders/image/avif/ encoder worker pool
387
- src/engine/asset/loaders/image/codec/JpegCodec.js JPEG behind Codec, last in the chain
388
- src/engine/asset/loaders/image/prototypePNG.js dev harness, up one level
389
- src/shade/renderer/loader/{gltf,usd}/ scene builders, unchanged in role
390
- src/format_tier_boundary.spec.js the rule
391
- ```
392
-
393
- ## Follow-ups this refactor surfaced, and where they stand
394
-
395
- - **`ImageDecoderWorker` learning JPEG — measured and declined** (2026-09-02). On 1024×1024 Sponza
396
- textures the platform decoder takes 6–11 ms wall with about 1.5 ms of that on the main thread; the
397
- in-tree decoder takes 128–178 ms wherever it runs, and a worker saves only the 1.5 ms. Fifteen to
398
- twenty times the latency for a read-back's worth of main-thread time is the wrong trade for a
399
- loader. The bench and its numbers are `shade/playground/image_decode_bench/`; `ThreadedImageDecoder`
400
- says why it sniffs PNG only. JPEG and AVIF reach the in-tree decoders only where no browser decoder
401
- exists.
402
- - **`usd_decode_image.js`'s private PNG decoder — taken, 2026-09-02** (separate session): `decode_png`
403
- now calls `PNGReader` and widens whatever it returns to RGBA8; the private inflate and its "Dynamic
404
- Huffman blocks not supported" throw are gone. The adapter stays with the loader. Spec beside it.
405
- - **The rest of review item C1 — done** (2026-09-02): `engine/asset/codec/DecodedImage.js` is the one
406
- result shape; `AvifCodec` sits last in the chain beside `JpegCodec`; `guessAssetType` maps `.avif`
407
- to `"image"`. The codec wraps `avif_to_sampler2d`, not the reverse — the adapter owns the HDR path
408
- and the reusable decoder context, and an 8-bit asset codec wants neither.
409
- - **KTX2 arrays and cube maps — the reader returns them** (2026-09-02). `TexelPayload` carries
410
- `layers` and `faces`, a level holds `layers * faces` images in KTX2's order, and the byte check
411
- accounts for them. `texel_data_from_ktx2` refuses layers and faces by name until `ShadeTexelData`
412
- grows a layer, and is the only place that changes when it does. The format tier still refuses a 3D
413
- array (no GPU API has one) and a Basis payload with layers (the transcoders take one image per
414
- level).
415
- - **A USD asset loader** — `load_usd` had no in-tree caller. A loader beside
416
- `GLTFSceneBundleAssetLoader`, with `guessAssetType` entries for the USD extensions, was spun off as
417
- its own task.
418
- - **Open:** `NativeImageDecoder.test` returns true for any bytes. A sniff (`PNG`, `0xFFD8`, `ftyp`)
419
- would let the chain be ordered by capability rather than by "the browser will probably manage".
@@ -1,142 +0,0 @@
1
- # Engine correctness bug hunt — 2026-07-24
2
-
3
- A targeted correctness sweep of the `meep` engine (`app/src/mir-engine/meep/src/`).
4
- Targets chosen from **architecture** (foundational / wide-blast-radius code) and **git commit
5
- history** (recently-landed, thinly-reviewed code). Method: multi-agent finder → adversarial
6
- verifier; every confirmed defect is pinned with a *failing* test before it is fixed.
7
-
8
- ## Ground rules
9
-
10
- - **Fix only what is provably wrong**, and only after a red test reproduces it. Everything
11
- uncertain is written up here rather than "fixed".
12
- - Black-box tests only — assert on observable behaviour, never on call counts / private paths.
13
- - No defensive guards for can't-happen conditions; no silent `try/catch`; correctness over bandaids.
14
- - `assert.*` is stripped in prod — do not test assert-rejections; real `throw` is testable.
15
- - Keep performance characteristics intact — a correctness fix must not regress the hot path.
16
- - Tests run from `H:/git/moh` via `node_modules/.bin/jest --config jest.conf.json <pattern>`.
17
-
18
- ## What is already covered (do not re-litigate)
19
-
20
- Per prior review memory, these were swept recently and validated clean or fixed:
21
- - **Physics core** + reactive (14-bug sweep, 2026-07) — determinism gate green.
22
- - **Sound / acoustic** simulator + render (Sopra / Steam-Audio-gap work).
23
- - **Network** core (GNS review) — P0s catalogued separately.
24
- - **engine review 2026-07-03** — physics/sound/network CLEAN; defects concentrated in
25
- ECS lifecycle + graphics bindings + serialization.
26
- - BVH2/8, clustered-lighting froxel geometry, path tracer, LPG stage correctness, EPA/GJK,
27
- fluid MAC, navmesh simplify — each has a standing spec gate.
28
-
29
- The hunt therefore targets **the delta since those reviews** and **areas with thin spec coverage**.
30
-
31
- ## Target map
32
-
33
- Tier 1 — recently landed (git history hot spots):
34
- - Gamepad input subsystem (`engine/input/devices/gamepad/*`, `GamepadDevice.js`)
35
- - Fog-of-war screen-space shader (`engine/ecs/fow/shader/*`)
36
- - Trail3D + tube geometry (`engine/graphics/ecs/trail3d/*`, `engine/graphics/trail/tube/*`)
37
- - Forward+ clustered light live updates (`engine/graphics/ecs/light/*`)
38
- - HUD edge-stick badge placement (`engine/ecs/gui/hud/*`)
39
- - Physics delta of the 14-bug sweep (adversarial check of changed files only)
40
-
41
- Tier 2 — foundational, wide blast radius (architecture):
42
- - `core/math/linalg`, `core/math/spline`, `core/math/statistics|random|noise`
43
- - `core/geom` transforms & primitives (Vector*, Quaternion, AABB, Matrix)
44
- - `core/collection` data structures
45
-
46
- Tier 3 — thin spec coverage:
47
- - `engine/animation` (16 specs / 83 src) — clip/curve binding, skinning
48
- - `engine/intelligence` (26 / 71) — behavior trees, blackboard, MCTS
49
- - `engine/navigation` (14 / 20) — pathfinding, path following
50
- - `engine/interpolation` (5 / 8) — interp snap was a deferred item
51
- - `engine/ecs` lifecycle (defects concentrate here per memory)
52
- - `engine/asset` loaders + serialization
53
-
54
- ## Hand-verified clean (independent of the agent sweep)
55
-
56
- These were read and checked directly (some with a throwaway empirical harness); no defect found:
57
-
58
- - **Gamepad stick calibration** (`GamepadStickCalibrator`, `GamepadStick`, `GamepadStickCalibration`,
59
- `apply_radial_dead_zone`, `gamepad_write_dpad_vector`) — robust median/MAD learning, self-correcting
60
- accepted-magnitude gate, continuous radial dead zone, correct D-pad vector. 22 passing tests.
61
- - **Tube parallel-transport frame** (`tube_frame_transport` + `v3_orthonormal_matrix_from_normal`) — the
62
- degenerate bootstrap correctly takes the perpendicular rows (util stores the input at rows 6..8), so N⊥T.
63
- - **Quaternion slerp** (`slerpQuaternions`) — correct shortest-path sign flip + lerp fallback near-parallel.
64
- - **Cubic animation curve** (`spline3_hermite`, `evaluate_two_key_curve`, `AnimationCurve.getKeyIndexLow`) —
65
- canonical Hermite basis; tangents scaled by segment dt; binary search boundaries correct.
66
- - **core/color** — empirical 200k-sample round-trip harness: YCoCg exact, YCbCr ≤1 LSB, HSV ~1e-16,
67
- OkLab ~1e-7, RGBE9995 matches the standard RGB9E5 algorithm exactly (the large minor-channel relative
68
- error is inherent shared-exponent quantization, not a bug). No defect.
69
-
70
- ## Findings
71
-
72
- The 23-area finder→adversarial-verifier sweep produced 26 candidate findings; 7 were rejected on
73
- verification, **18 confirmed** (real bug · reachable · node-testable), and **1 uncertain** (real logic
74
- defect but effectively unreachable on the shipped path — see below). Each confirmed item below was
75
- re-checked against source and pinned with a *failing* test before fixing.
76
-
77
- ### Confirmed & fixed (test-first)
78
-
79
- | # | Severity | Area | File | Bug | Status |
80
- |---|----------|------|------|-----|--------|
81
- | 1 | high | statistics | `core/math/statistics/gaussian_amplitude.js` | Squared the variance in the exponent (passed `variance` to `gaussian()`, which squares its arg) → PDF ~45% off for any variance≠1 | ✅ fixed |
82
- | 2 | med | statistics | `core/math/statistics/computeStatisticalMean.js` | Loop started at `0` not `start`, but divided by `end-start` → wrong mean for any non-zero start | ✅ fixed |
83
- | 3 | med | linalg | `core/geom/Quaternion.js` (×3: XYZ/YXZ/ZYX) | `Math.asin` unclamped → **NaN at gimbal lock** (fp overshoot past 1). Hits `copy_transform_to_threejs_object` + first-person yaw seed | ✅ fixed |
84
- | 4 | med | geom | `core/geom/ConicRay.js` | `sampleRandomDirection` NaN for a cone axis of exactly `(0,0,-1)` (k=1/(1+dZ) singular at the antipode) | ✅ fixed |
85
- | 5 | low | geom | `core/geom/2d/line/line2_compute_segment_point_distance_sqr.js` | Zero-length segment → `clamp01(0/0)=NaN` (3D twin already guards this) | ✅ fixed |
86
- | 6 | med | collection | `core/collection/RingBuffer.js` `getHead()` | Returns `undefined` when head wraps to slot 0 on a full buffer (no wrap, unlike `getFromHead`) | ✅ fixed |
87
- | 7 | med | collection | `core/collection/RingBuffer.js` `contains()` | Scans whole backing array → reports `true` for `shift()`-ed-out elements | ✅ fixed |
88
- | 8 | low | binary | `engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.js` | Signed `>>2` corrupts a 32-bit dict back-ref offset ≥2²⁹ (should be `>>>2`) | ✅ fixed |
89
- | 9 | low | binary | `core/binary/EncodingBinaryBuffer.js` | Signed `>>1` corrupts interned-string back-ref ≥2³⁰ (should be `>>>1`) | ✅ fixed |
90
- | 10 | low | binary | `core/binary/BinaryBuffer.js` `writeASCIIString` | Boundary `> 0x80` lets non-ASCII U+0080 pass validation (should be `> 0x7F`) | ✅ fixed |
91
- | 11 | high | ecs | `engine/ecs/EntityComponentDataset.js` `traverseEntities` | Uses **module-shared scratch arrays** → nested traversal corrupts the outer loop (wrong-type component delivered) | ✅ fixed |
92
- | 12 | high | navigation | `engine/navigation/ecs/path_following/PathFollowingSystem.js` | `next_offset > path_length` (should be `>=`) → non-looping follower snaps to start & never finishes when a step lands exactly on the end | ✅ fixed |
93
- | 13 | med | navigation | `engine/navigation/ecs/components/Path.js` | `sample_linear`/`sample_catmull_rom` return `(t<0 && …)` — always false for valid offsets → `estimatePathViaIterativeIntegral` returns 0 | ✅ fixed |
94
- | 14 | med | input | `engine/input/ecs/systems/InputControllerSystem.js` | `link()/unlink()` ignore `enabled` → binding double-registration → double input dispatch after a disable/enable round-trip | ✅ fixed |
95
- | 15 | med | graphics | `engine/graphics/trail/tube/TubeX.js` | `rotate()` writes recycled segment with reversed winding → lit tubes shaded from the wrong side | ✅ fixed |
96
- | 16 | med | graphics | `engine/graphics/render/forward_plus/LightManager.js` | Reused sorted-lights array never shrinks → stale light records re-uploaded to the light-data texture every frame | ✅ fixed |
97
- | 17 | med | animation | `engine/animation/clip/ecd_bind_animation_curve.js` (fix) | LINEAR quaternion tracks nlerp'd component-wise with no hemisphere alignment → long-way rotation on `dot<0` key pairs | ✅ fixed |
98
- | 18 | low | fow | `engine/ecs/fow/FogOfWar.js` `conceal()` | No dedup guard (unlike `reveal()`) → conflicting fade rows on one cell | 📝 documented (see below) |
99
-
100
- 19 confirmed defects were addressed test-first (18 from the sweep + the one below that the verifier
101
- had flagged as "uncertain"). Each fix landed in its subsystem commit with a red-before-green test.
102
- `#3` clamps all three Euler extractors; `#11` also removes the now-dead scratch pools; `#13` fixes both
103
- `Path.sample` and its consumer `estimatePathViaIterativeIntegral`.
104
-
105
- ### Also fixed (verifier had marked "uncertain")
106
-
107
- - **`engine/animation/keyed2/AnimationTrackPlayback.js` — `on.ended` never fired when a frame landed
108
- exactly on `timeEnd`.** Genuine logic defect: the strict `> __nextKeyTime` guard skips the exact-landing
109
- frame, and the next frame fails the `position < timeEnd` guard, so the one-shot `ended` never dispatched.
110
- Fixed by adding `|| newPosition >= timeEnd` to the outer guard (mid-track exact landings never reach
111
- `timeEnd`, so they are unaffected; fire-once preserved). **Reachability caveat, recorded for the owner:**
112
- the only in-engine driver (`ScriptSystem`, variable-rate float time) makes an exact landing a measure-zero
113
- event, so this hardens the public primitive rather than fixing an observed in-game leak.
114
-
115
- ### Not auto-fixed — owner decision required
116
-
117
- - **`#18` `FogOfWar.conceal()` missing dedup.** `reveal()` is idempotent (it guards on `samplerData === 255`
118
- and immediately moves the cell off that state). `conceal()` guards only `value !== 255` and does **not**
119
- move the cell, so a second `conceal` — or a `conceal` while the cell is mid-`reveal` — stacks a second,
120
- opposing fade row on the same cell; the two rows then fight and the final visibility is decided by fade
121
- timing, not by the last command (plus a one-frame flicker). **Why deferred:** (1) no in-engine caller uses
122
- `conceal()` (`FogOfWarRevealerSystem` only reveals), so it is public-API-only, LOW severity; (2) the
123
- *correct* conflict semantics are a design choice (should `conceal` cancel an in-progress `reveal`, or be
124
- ignored?); and (3) an efficient "one fade row per cell" guard needs a cell→row index that `RowFirstTable`
125
- does not provide — a naive per-cell scan of `fadeMask` is `O(rows·cells)` and could be costly for large
126
- concealed regions. **Recommended fix:** enforce "at most one fade row per cell, latest command wins" by
127
- updating an existing row's `speed`/target in place instead of adding, backed by a cell→row map.
128
-
129
- ### Follow-up noted (not a regression)
130
-
131
- - **`#17` CubicTangents quaternion tracks.** The hemisphere-alignment fix covers LINEAR tracks (glTF's
132
- default). GLTF cubic-spline (`CubicTangents`) quaternion tracks pack in/out tangents per key and would need
133
- those tangents negated alongside the value; left as a follow-up.
134
-
135
- ### Pre-existing failures (NOT caused by this sweep)
136
-
137
- - `engine/control/first-person/WallRunScenarios.spec.js` has 4 failing "unhappy path" tests. Confirmed
138
- pre-existing by re-running the suite against the original `Quaternion.js` (identical 4 failures). The
139
- commit that added them ("WallRun scenarios + 3 gaps they surfaced") marks them as gap-documenting tests
140
- for unimplemented behaviour. Out of scope here.
141
-
142
- Full suite after the sweep: **8846 passed, 4 failed (the pre-existing WallRun gaps), 118 skipped.**