@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,481 +0,0 @@
1
- # Virtual Geometry — plan
2
-
3
- Scheduled and deferred work. [VIRTUAL_GEOMETRY_DESIGN.md](./VIRTUAL_GEOMETRY_DESIGN.md) pins
4
- what has been *decided*; [VGEO_FORMAT.md](./VGEO_FORMAT.md) specifies what is *written*.
5
- This file holds what is *next*, and — more usefully — the reasoning and the source for
6
- ideas that are accepted but not yet scheduled, so that picking one up does not mean
7
- rediscovering it.
8
-
9
- The GPU runtime is **built** — `runtime/`, and
10
- [runtime/RUNTIME_PLAN.md](./runtime/RUNTIME_PLAN.md) is its design and its adversarial review. The
11
- cut runs on the device, the VRAM tier holds pages under a budget, wants and touches come back
12
- through a readback, and a VG instance enters the meshlet pipeline through a door of its own. §3's
13
- items are no longer blocked on "a runtime"; each names what it is actually waiting for now.
14
-
15
- Items carry their provenance. `[M §x]` is a section of
16
- [MICRON_COMPARISON_2026_08_20.md](./MICRON_COMPARISON_2026_08_20.md), the review of the
17
- prior WebGL implementation at
18
- `H:\git\moicon\prime\src\components\Viewer\Viewer.Meep\Micron\`. `[V §x]` is a section of
19
- [VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md](./VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md), the
20
- review of [nvpro-samples/vk_lod_clusters](https://github.com/nvpro-samples/vk_lod_clusters).
21
-
22
- ## 1. Landed
23
-
24
- For the record, so that a later reader can tell an accepted-and-done item from an
25
- accepted-and-waiting one.
26
-
27
- - **Accumulated quadrics carried from the source to the root** `[M §2.1]`, `[V §3.1]`.
28
- Vertex quadrics are built once from the source triangles and threaded through every
29
- round; error is the accumulated quadric evaluated at the vertex's final position,
30
- normalized by its weight, which makes it an object-space distance from the *source*
31
- rather than a sum of per-round residuals. `bt_mesh_simplify_with_quadrics` in
32
- `core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js`; `Quadric3.distance`.
33
- - **Cluster-count simplification target** `[M §2.2]`, and a minimum cluster fill through the
34
- generalized leaf repack `[V §3.3]`.
35
- - **Compactness-weighted cluster adjacency** `[M §2.3]` — `compactness()` in
36
- `build/level/vgeo_build_levels.js`.
37
- - **Speculative groups, rejected individually** `[M §2.4]`.
38
- - **Per-level summary table in the file head** `[V §4.1, §6.1]` — VGEO_FORMAT §4.1.
39
- - **A traversal metric on the page DAG, and a group tree inside each page** `[V §2.1, §6.3]` —
40
- VGEO_FORMAT §6.1–6.2. See §2.2 below for why, which is worth keeping: the reasoning corrects
41
- an error this file previously made.
42
- - **Boundary constraint quadrics** — see §4.1, which was the blocking item.
43
- - **A partitioning fallback** `[M §3.3]`, `[V §3.2]` — see §2.1.
44
- - **An incremental reader, and residency with it** — `format/read/`. A container opens in its
45
- header, level table and root frame; after that a page arrives only because something asked for
46
- it, from exactly the byte range the page pointing at it wrote down. `VGeoContainerReader`
47
- holds residency, activation and eviction; `VGeoByteSource` is the seam — an `ArrayBuffer`, a
48
- dropped `Blob`, or HTTP ranges that refuse a `200` where they asked for a range. The
49
- whole-file audit is a tool in `format/read/tool/` and nothing on the runtime path can reach it;
50
- format §4/§5's reject rules always run.
51
- VGEO_FORMAT §10 marks, requirement by requirement, what this covers and what it leaves to a
52
- budget owner.
53
- - **The reader hardened for 1000 assets, and the budget owner it was waiting for** —
54
- `virtual/residency/`. Measured first, and three of the audit's suspicions did not survive the
55
- measurement, which is the more useful half of the result: the per-page allocation the reader adds
56
- on top of a decoded page is 6.4–11.9 KB, which is 1.2% of a 544 KB page and not worth a rewrite;
57
- the reverse-reference index tracks the residency frontier rather than the session's history, so it
58
- does not leak; and the pinned root page is one cluster by invariant §11.7, measured at 1,472 and
59
- 1,732 B, so a thousand of them is under 2 MB and no builder-side cap is wanted. What *was* real:
60
- a 256 KiB head prefix retained for the reader's life outside every budget (released now, and the
61
- default cut to 64 KiB), no retry/backoff/quarantine so one bad page threw (now §10's full
62
- sequence, with telemetry), no cancellation at all (now a per-read `AbortSignal` end to end), and a
63
- whole-file audit and install-every-page sweep reachable from a reader a renderer holds (now tools,
64
- with a 1 GiB bound and a message instead of an allocator failure). Above the reader,
65
- `VGeoResidencyManager` owns the byte budget, the touch stamps, the eviction order and — through
66
- one `core/process/ConcurrencyGate` — one fetch cap across every reader, because a per-reader cap
67
- does not compose, measured at sixteen concurrent requests from four readers capped at four.
68
- - **Activation, and the double-draw it prevents** — format §10's selectable invariant, as one
69
- live-reference counter per resident group with cascades on install and evict. The clamp reads
70
- *selectable*, not *resident*: a group whose parents are split across pages must not be refined
71
- into through the parents that happen to have arrived. Measured on a 19-page bunny, 21 of whose
72
- 362 non-root groups have split parents: 355 of 576 camera positions double-drew, up to 10.7%
73
- of drawn triangles, before. Zero after. It reads as corrupt geometry rather than as a missing
74
- page, which is why the tool that found it could not name it.
75
- - **A CPU cut against a partially resident container** —
76
- `runtime/cut/__fixtures__/vgeo_select_cut.js`. It is the runtime's rule rather than a debug
77
- approximation of it, and `vgeo_select_cut.spec.js` holds it to the format's guarantees. A
78
- `vgeo_viewer` playground used to draw it on screen through Shade's direct geometry path, which is
79
- what made clamping, streaming and eviction watchable; once `playground/vgeo_runtime/` could show
80
- all of that through the meshlet pipeline, the page was a second renderer and a second streaming
81
- loop for no gain, and it was retired. The traversal stayed, because it is the oracle.
82
-
83
- - **The GPU runtime** — `runtime/`, and [runtime/RUNTIME_PLAN.md](./runtime/RUNTIME_PLAN.md) for the
84
- design and the adversarial review that reshaped it. The cut is a wavefront BFS over the page DAG
85
- with a per-cluster predicate at the leaves, and that combination is the whole design: the
86
- hierarchy decides which clusters to *look at*, the predicate decides whether to *draw* them, and
87
- because the predicate is a pure function of one row, a page visited twice costs a duplicate record
88
- rather than a wrong answer — so dedup is one bit per (instance, resident page) and never per
89
- cluster, which is the number design §4 is trying not to pay.
90
-
91
- Three things the review found, in descending order of how much they cost to rediscover:
92
-
93
- - **Design §4's per-cluster rule double-draws as written.** A group that is resident but not
94
- selectable has clusters satisfying it while their coarse stand-in is drawn over the same
95
- surface. The rule needs an *own group selectable* term that a traversal gets for free and an
96
- evaluator does not. §6 states the fact without stating it as a term of the rule, because §4 was
97
- written for a traversal.
98
- - **Page size decides whether that state is even reachable.** At the 8 KB pages
99
- `vgeo_select_cut.spec.js` uses, a torus puts exactly one group in every page — so a blocked group is
100
- a blocked page, no page is ever mixed, and no page is ever entered twice. Measured: 40 pages
101
- withheld one at a time and 60 random residency subsets produced *no* mixed page. At 64 KB the
102
- median page holds eight groups and it is ordinary. A spec for anything about mixed selectability
103
- has to be written at a page size that produces it.
104
- - **Every bound degrades in one direction.** A full meshlet collection drops records; a claim
105
- allocation that does not fit drops the *instance*, because an instance run unclaimed draws its
106
- whole cut once per parent page that reaches it.
107
-
108
- Held against mutation: removing the own-group term, the claim, the child selectability check or
109
- the parent projection each turns the agreement suite red. Proved on a device in
110
- `playground/vgeo_runtime/` — a 13-page container opened holding only its root page, streamed to 9
111
- by its own wants, 50 drawn clusters at τ=0.5 falling monotonically to 8 at τ=16, clamping to zero,
112
- dropping nothing.
113
-
114
- ## 2. Next
115
-
116
- §2.1 and §2.2 both landed. They are kept because their notes are the reasoning behind decisions
117
- that are now only visible in code, and rediscovering them is the expensive part. §2.3 **half**
118
- closed, and the half that did not is the more interesting one — read it before building a
119
- prefetch.
120
-
121
- ### 2.1 A partitioning fallback for when METIS fails `[M §3.3]`, `[V §3.2]` — LANDED
122
-
123
- `core/graph/graph_partition_spectral.js` — recursive spectral bisection over CSR, matrix-free,
124
- signature-compatible with `metis_partition_kway`. `build/partition/vgeo_partition_graph.js` tries METIS
125
- and falls back to it; both of the builder's partitioning sites go through that wrapper.
126
-
127
- Notes worth keeping:
128
-
129
- - **Matrix-free, deliberately.** The Fiedler vector comes from power iteration on `d·I − L`
130
- with the constant projected out each step; `L` is applied, never built. A dense
131
- formulation — which is what made Micron's third partitioner unattractive to port — is
132
- O(n²) in memory and unusable at the sizes that make a fallback necessary.
133
- - **It is a floor, not a competitor, and the code says so.** The shift needed to make the
134
- operator positive leaves the eigenvalues of interest within a fraction of a per cent of
135
- each other, so a graph with no real bottleneck converges only approximately. Measured: a
136
- 20×20 grid cuts at 23 edges against an optimum of 20. Iterations are 256, chosen where the
137
- curve flattens — the same grid's four-way cut is 82 edges at 64 iterations, 47 at 256, 48
138
- at 1024. Balance is exact at every setting; only the cut moves.
139
- - **METIS is much harder to make fail than expected**, and this is insurance rather than a fix
140
- for an observed problem. Probing it with NaN and negative edge weights, adjacency indices
141
- past the end of the graph, more parts than vertices, zero parts, and edgeless graphs
142
- returned a partition every time. The only inputs that stop it malform the CSR itself, which
143
- is a caller bug. Worth knowing before anyone spends effort here again.
144
-
145
- Still open, and related: meshopt's builder treats a group as **stuck** when simplification
146
- leaves more than `merged.size() * simplify_threshold` faces, and escalates to permissive, then
147
- sloppy modes, inflating the recorded error to pay for it. We have no middle rung between
148
- "simplified" and "refused". This is now safe to build — it was not before, because a sloppy
149
- mode that erodes boundaries needed §4.1 first, and §4.1 has landed.
150
-
151
- ### 2.2 A traversal metric on the page DAG, then nodes within a page `[V §2.1, §6.3]` — LANDED
152
-
153
- **Correcting an earlier framing in this file.** A previous revision called paging "the open
154
- question" for a node hierarchy, on the grounds that nvpro never had to answer it — their node
155
- array is per-geometry and resident, and at 1 G triangles ours would be tens of megabytes.
156
- Both halves of that were wrong, and the second one was wrong in a way that hid the actual
157
- work.
158
-
159
- **The pages already form a rooted DAG, and it is already validated.** Every one of the
160
- properties a traversal wants from a hierarchy is a rule the format states and
161
- `vgeo_validate_container` enforces:
162
-
163
- - rooted at page 0, and *every page is reachable from it through child tables* (§11.3);
164
- - acyclic — a child table's `frame_offset` is strictly greater than the referencing page's,
165
- so file order is a topological order (§11.3);
166
- - levelled — a cluster's child group is always in a page whose band tops out strictly below
167
- its own page's (§11.4);
168
- - and a group never spans a page, so an edge is a whole LOD decision, not part of one.
169
-
170
- It is a DAG rather than a tree, and measurably so. On a 98k-triangle sphere at 32 KB pages:
171
- **91 pages, 291 edges, in-degree up to 7, 82 of the 91 pages have more than one parent, none
172
- unreachable, longest path from the root 12.** At 8 KB pages a 24k sphere gives 102 pages and
173
- 182 edges with 68 multi-parent pages. It degenerates to a chain only when every band fits in
174
- one page, which is to say only on assets too small for any of this to matter.
175
-
176
- That the page DAG is a *useful* coarsening rather than an arbitrary one rests on something
177
- already in the builder: groups are laid into pages in Morton order of their centroid within a
178
- band (`vgeo_assemble_pages`). A page is therefore spatially local, and a bounding volume over
179
- one means something. Pack pages arbitrarily and every page bound covers the model.
180
-
181
- **Nanite does exactly this combination**, which settles the compatibility question outright.
182
- Cluster data lives in a paged, streamed virtual byte buffer with a separate hierarchy;
183
- nodes form a BVH within a mesh connecting clusters across detail levels; a persistent traversal kernel walks
184
- it on the GPU from an MPMC queue, where only nodes generate new work and clusters are only
185
- consumed. And when a child's memory is not present, **processing stops at the parent and a
186
- request is made for the child** — a lower-detail LOD is what you see until it arrives. That
187
- is the same mechanism as nvpro's invalid-address-is-the-request-slot (§2.3 of the
188
- vk_lod_clusters review), and it is the answer to "what does a traversal do about paging":
189
- nothing special. It stops.
190
-
191
- #### What is actually missing
192
-
193
- Not a hierarchy. A **metric on the one we have**. A child-table entry today carries
194
- `frame_offset`, `page_index`, `fetch_size`, `decoded_size` and four reserved bytes — enough
195
- to fetch a page, and nothing at all with which to decide whether to. So the DAG is
196
- traversable but not *cullable*: the only way to learn that a page was not wanted is to fetch
197
- it and decode it, which is precisely backwards. The decision worth making cheaply is the one
198
- before the fetch.
199
-
200
- The work is two steps, and both are natively paged because they live inside structures that
201
- already are:
202
-
203
- **Step 1 — a traversal metric per child-table entry.** The child page's LOD-metric sphere and
204
- its error range, 24 bytes, taking the entry from 24 to 48. It buys, in one change: frustum and
205
- LOD culling of a whole page and everything reachable only through it; a want-list that is
206
- exactly the pages that passed the test and are not resident; and prefetch depth as a policy
207
- dial instead of a guess. The page header carries the same metric for its own content, so an
208
- installed page can be re-tested without walking back to its parent.
209
-
210
- **Step 2 — a node tree within a page, over that page's own groups.** Finer culling and more
211
- width at the top. This one has *no* paging question by construction: it is inside a page, it
212
- ships and evicts with it, and it cannot dangle.
213
-
214
- Both landed together; VGEO_FORMAT §6.1–6.2 specifies them, `build/page/vgeo_build_page_nodes.js`
215
- builds them. Notes worth keeping:
216
-
217
- - The tree needs no partitioner. A page's groups arrive in Morton order of their centroid, so
218
- a balanced split of that order is already a spatial split.
219
- - A node's metric comes from the groups under it and needs no new geometry: every cluster of a
220
- group shares the sphere and error of the step that consumed it — the same fact that makes
221
- the group the atomic LOD decision — and monotonicity makes that one sphere cover everything
222
- in it.
223
- - Fanout 4, breadth-first, children contiguous, parents at lower indices than children.
224
- Measured across spheres from 3k to 98k triangles at 8 KB–256 KB pages: **0.3%–1.6% of asset
225
- size, trees one to three deep**.
226
- - The validator earns its keep here. A node whose sphere does not cover its subtree drops
227
- geometry that nothing downstream would notice, and a child-table metric is a *copy* that can
228
- drift from the page it describes without that page being wrong. Both are checked, and both
229
- checks have corruption tests behind them (VGEO_FORMAT §11.10–11.11).
230
-
231
- #### On width at the root
232
-
233
- The concern that a descent from one root cluster starves a GPU dispatch was overstated.
234
- Nanite's queue is seeded with *the root cluster for each instance*, so width comes from the
235
- instance count first and from fan-out immediately after — measured out-degree reaches 10 by
236
- the middle bands of the 98k sphere. It bites only for a single instance in close-up, and only
237
- for the first few bands, which are also the cheapest to walk.
238
-
239
- ### 2.3 The node tree has a reader; the child-table metric still does not — LANDED (half)
240
-
241
- §2.2 built both, and the format specifies both: every child-table entry carries the child page's
242
- LOD-metric sphere and error range (VGEO_FORMAT §5, §6.2), and every page carries a fanout-4 tree
243
- over its own groups (§6.1). The writer emits them, `vgeo_validate_container` checks them
244
- (§11.10–11.11) with corruption tests behind both checks, and **nothing reads either**.
245
- `VGeoPage` records `node_count` and stops; `vgeo_select_cut` descends group by group.
246
-
247
- This is not a defect to fix on its own account. Building a consumer for its own sake would fix
248
- the interface before the pass that has to live with it, and the CPU traversal does not need the
249
- acceleration — it walks a resident page set on a machine with branches. It is written down here
250
- for two reasons: because "the validator checks it" reads exactly like "something uses it", and
251
- because the cost is already paid — 0.3%–1.6% of asset size, on disk, in every asset already
252
- built, earning nothing.
253
-
254
- What it buys when the pass arrives, and what to hold it to:
255
-
256
- - **Before the fetch.** A page can be frustum- and LOD-culled from its parent's child-table
257
- entry, so a want list is exactly the pages that passed the test and are not resident, rather
258
- than everything a descent happened to reach. Today the only way to learn a page was not wanted
259
- is to fetch and decode it, which is backwards.
260
- - **Below the page.** A node discards a region rather than a cluster, and gives the top of the
261
- descent width it does not otherwise have.
262
-
263
- Neither is measurable until there is a GPU traversal to measure, which is the honest reason it
264
- has waited.
265
-
266
- **What happened when the traversal arrived**, and it is not what this section predicted.
267
-
268
- *Below the page*, the node tree has its reader. `runtime/cut/shader_vgeo_cut_traverse` descends it,
269
- tests each node's sphere and `max_parent_error`, and discards a region rather than a cluster. The
270
- cull fires across the whole agreement sweep and changes not one selected cluster, which is what
271
- sound means for an accelerator. The 0.3%–1.6% of asset size is earning something now.
272
-
273
- *Before the fetch*, the child-table metric **still has no reader, and now for a reason rather than
274
- for want of a consumer.** Held against the cut, it buys nothing. A want is emitted from a cluster
275
- that has already passed `project(parent) > τ` and `project(self) > τ`; the child page's metric
276
- covers that cluster's child group among others, so its `max_parent_error` is at least that group's
277
- `parent_error` and its sphere contains that group's `parent_sphere` — and the projection is monotone
278
- in both. The page-level test is therefore **always** above τ at exactly the moment the question is
279
- asked. It cannot reject what a per-cluster test has just accepted, because the per-cluster test is
280
- strictly tighter. The frustum half dies the same way: the cluster's own `self_sphere` is a tighter
281
- test than the child page's union sphere, available locally, for free.
282
-
283
- Where the copy *is* load-bearing is **prefetch** — ranking and bounding pages nobody has asked for
284
- yet, which by definition has no cluster-level evidence to be tighter than. `min_self_error` is in
285
- the same position: it ranks a want and the cut never asks it anything. So the field is not dead, it
286
- is early, and the thing it is early for is §3's territory rather than the cut's. Anyone building a
287
- prefetch should start here.
288
-
289
- ## 3. Was waiting on a GPU runtime
290
-
291
- Accepted, documented here so the reasoning is not lost. "Waiting on a runtime" used to mean waiting
292
- on all of it; the runtime exists now (`runtime/`), so §3.1 has landed with it and the rest name what
293
- they are actually waiting for.
294
-
295
- ### 3.1 Object-space refinement `[M §2.7]` — LANDED
296
-
297
- `RefinementSpec.build` in `Micron/render/refinement/RefinementSpec.js`: multiply the camera's
298
- view-projection by the instance transform once, derive the frustum planes from the result,
299
- and run every cull and error test in **object space** against untransformed cluster bounds.
300
- One matrix multiply per instance instead of transforming every cluster's bounds to world
301
- space. With thousands of instances sharing one geometry that difference is the entire cost.
302
-
303
- **Landed** with the cut. `runtime/cut/shader_vgeo_cut_collect` is the one place a VG transform is
304
- touched: it multiplies the camera's view-projection by the instance transform, extracts the frustum
305
- from the result, inverts the transform once for the eye, and writes both into the instance record.
306
- Every cull and error test downstream runs against untransformed cluster bounds.
307
-
308
- One thing fell out that `RefinementSpec.build` never had to face, because object space is not free
309
- when an instance is scaled: error and distance are **both** object-space, so a *uniform* scale
310
- cancels out of `r_e · k / d` exactly and needs no term at all. A non-uniform one does not, and the
311
- correction is the ratio of the largest axis scale to the smallest — conservative, so it over-refines
312
- rather than under-refines, and exactly 1 for every instance that is not stretched. Worth knowing
313
- before anyone adds a scale term that does nothing in the common case.
314
-
315
- ### 3.2 Proxy geometry from a DAG cut `[M §2.6]`
316
-
317
- `Micron/format/micron_build_proxy_geometry.js`: refine from the root with a heap keyed on
318
- `-patch.lod` until a target polygon fraction is reached, and emit the result as an ordinary
319
- mesh. Shadows, physics, raycasts, editor previews and impostors all want a cheap whole-object
320
- mesh, and a cluster DAG hands one over for the cost of a heap walk.
321
-
322
- Two consumers make this more than a convenience:
323
-
324
- - A **shadow or secondary view** that does not justify its own cut can take a proxy instead
325
- (§3.3).
326
- - Anything outside the renderer — physics, navigation — that wants geometry without
327
- understanding clusters.
328
-
329
- ### 3.3 One cut per view, and proxies where a cut is not worth it
330
-
331
- **Correcting the design.** VIRTUAL_GEOMETRY_DESIGN §4 claimed the cut is "view-agnostic in
332
- *coverage*" and that keeping it so is "what lets shadow views reuse it". That is wrong, and
333
- the design has been corrected: virtual geometry is view-*dependent* by construction. A cut
334
- selected for the main camera is selected at the main camera's distances and pixel budget, and
335
- is simply the wrong cut for a shadow view — a light a hundred metres behind the camera wants
336
- different clusters, and a cascade wants a different set again. Epic acknowledge computing
337
- several cuts per frame for exactly this reason.
338
-
339
- So the plan is **a cut per view**, with the setup shared rather than the result: the traversal
340
- kernel, the residency state, and the page working set are all view-independent, and only the
341
- projection and threshold differ. That half is built — a cut runs per expansion site per view, and
342
- what it costs is measured in CUT_PLAN_2026_09_14 §0, which also plans to keep each view's cut
343
- between frames rather than re-walking it from the root. And where a view does not justify a cut
344
- at all — a distant cascade, a low-resolution reflection — it should be able to take a proxy
345
- (§3.2) instead. That choice belongs to the view, not to the geometry.
346
-
347
- ### 3.4 GPU-driven allocation `[V §4.3]`
348
-
349
- **Narrower now, and the runtime says exactly how much.** A VG page is one arena allocation holding
350
- its node, group and cluster rows back to back, and an eviction frees one allocation and writes one
351
- lookup word. Nothing inside a resident page can move, growth appends through `OffsetAllocator#grow`
352
- and relocates nothing, and no address migrates. What VG's churn *does* still reach is
353
- `GPUMeshletManager`: a page install is an `add_batch` and an eviction a `remove_batch`, so the holes
354
- accumulate in that pool and closing them is still a CPU-side rebuild with the unhandled
355
- address-migration TODO downstream of it. Pages are near-uniform in size, so a hole is usually
356
- re-usable by the next install — which narrows the problem without closing it, and is the reason this
357
- is still an item rather than an incident.
358
-
359
- Worth pursuing **beyond virtual geometry** — this is a GPU-driven engine and CPU-side
360
- allocation for GPU-resident data is a standing mismatch.
361
-
362
- `vk_lod_clusters/docs/clas_allocation.md` reports host-side size estimates running "often 3-5x
363
- bigger than actual" and moves allocation on-device, reading back true sizes. Two designs are
364
- given: naive dense compaction (discouraged — "high memory movement bursts") and a
365
- **device-resident allocator that relocates an allocation once, after construction**, with a
366
- free-gap list maintained on the GPU —
367
- `shaders/stream_allocator_build_freegaps.comp.glsl`,
368
- `shaders/stream_allocator_freegaps_insert.comp.glsl`,
369
- `shaders/stream_allocator_load_groups.comp.glsl`,
370
- `shaders/stream_allocator_unload_groups.comp.glsl`.
371
-
372
- The mirror-image problem on our side is recorded in design §2: `GPUMeshletManager`'s
373
- *compaction* is a CPU-side rebuild costing ~2× VRAM transiently, and meshlet-address migration
374
- is an acknowledged unhandled TODO in `GPUGeometryManager`. Growth has stopped being a rebuild
375
- since this was written — it appends through `OffsetAllocator#grow` and moves no record — which
376
- narrows the problem to exactly the case VG churn produces: closing holes, not adding room.
377
-
378
- ## 4. Was blocking
379
-
380
- ### 4.1 Boundary constraint quadrics — LANDED
381
-
382
- Was the single most important open item, and had been listed as a required simplifier
383
- extension in design §2 from the start. It became load-bearing rather than latent once the
384
- round loop began converging through boundary erosion: an asset built over open geometry
385
- recorded error that was not merely optimistic but wrong. Measured before the fix: 400 loose
386
- triangles built a root of 25 triangles carrying `self_error = 0`, and `project((s, 0)) ≡ 0`
387
- means such a root is selected at any distance and never refined. Nothing downstream could
388
- catch it — the validator checks monotonicity, and zero is monotonic.
389
-
390
- `bt_mesh_compute_vertex_quadrics` now adds, for every edge whose face count is not two, the
391
- plane through it perpendicular to its face, weighted. A face-plane quadric says only how far a
392
- vertex has moved *off* the surface and by construction says nothing about it sliding *along*
393
- one; for an interior vertex that is right, and for a vertex on a border it is exactly wrong.
394
- Non-manifold edges are treated the same way and for the same reason — a seam is no freer to
395
- move than a border.
396
-
397
- Two things fell out that are worth knowing:
398
-
399
- - **Weight 3, measured.** The error a vertex reports is the mean over its planes, so a border
400
- vertex with a handful of faces and two constraint planes has the retreat diluted threefold
401
- at weight one. Three brings it back to roughly the displacement itself while leaving the
402
- border free enough to erode at all — which it must, or a mesh of open shells has no coarse
403
- form and no root.
404
- - **The measurement had a second hole, and it was the larger one.** Taking the error as the
405
- max over *surviving* vertices misses geometry that vanished outright: a patch consumed
406
- whole leaves a vertex belonging to no face, which is swept away by cleanup, and that vertex
407
- is the only thing still holding the quadric of what used to be there. A triangle soup
408
- simplifies by *deleting* triangles, so this reported the loss of most of a model as free
409
- even with boundary quadrics in place. `bt_mesh_simplify` now measures before the cleanup
410
- and returns the object-space distance the surface moved — which is also a better return
411
- value than the raw squared residual it used to give, and nothing outside its own specs was
412
- reading that.
413
-
414
- Result: the 400-triangle soup now reports 0.22 at its first merge rising to 0.39 at the root,
415
- on triangles about a unit across. A **flat** sheet still reports exactly zero at every level,
416
- which is the other half of the contract — a flat surface is exactly representable at any
417
- triangle count, and a vertex partway along a straight border slides off without changing the
418
- outline. Its corners are not free, which is what stops it collapsing to nothing.
419
-
420
- ### 4.2 Payload size
421
-
422
- Deferred deliberately; it is a separate campaign. Two threads, in the order they should be
423
- attacked:
424
-
425
- - **Mantissa-bit dropping** `[V §3.5, §6.6]`. The nvpro README recommends dropping low
426
- mantissa bits of positions and UVs ahead of its lossless group compression. It keeps the
427
- float32 layout, the metadata struct and every decode shader exactly as they are, and simply
428
- makes the bytes compress. A fraction of the work of quantization for a real share of the
429
- benefit, and it composes with the page codec, which has since shipped: LZ4 finds only
430
- 1.12x–1.18x on today's payloads *because* float32 positions give an LZ matcher nothing, and
431
- dropping mantissa bits is how you give it something.
432
- - **Cluster-local position quantization**, as design §2 describes — invasive, spanning
433
- encode, the metadata struct and every decode shader.
434
-
435
- ## 5. Considered and declined
436
-
437
- - **Backface normal-cone culling** `[M §2.3]` — the `cone_reserved` field, a producer for it,
438
- and the cull pass that would read it. **Declined: the HZB already rejects what a cone would,
439
- and it does it on evidence rather than on a bound.** On a closed surface backfacing *is*
440
- occluded, and §7's two-pass HZB sees that; what a cone adds over it is pre-emption by one
441
- pass, bought with per-cluster data and a per-cluster test in the cull pass, every frame and
442
- per view. It pays best where cones are tight — flat clusters, which are also the cheapest
443
- to raster — and degrades exactly where a cut spends its clusters, because a group's cone
444
- widens toward hemispherical as the levels coarsen. Micron paid for tight cones by scoring
445
- merges on cone angle (`[M §2.3]`), which is the real price: grouping stops being a purely
446
- spatial criterion in order to feed a cull that the HZB was already doing.
447
-
448
- This is not our finding alone, which is why it is a decline rather than an experiment: Epic
449
- did not ship it in Nanite, meshoptimizer's author argues against the payoff despite the
450
- library exposing cone data, and Remedy report the same for Northlight. Taken together with
451
- the mechanism above, there is nothing left to measure.
452
-
453
- Consequences, so that nobody re-derives them: `cone_reserved` was a zeroed reserved u32 until
454
- `format_version` 5 (format §7) — reclaiming 4 bytes of 112 was never worth a version move on its
455
- own, and it went when `subtree_aabb` needed 24 and the version moved anyway. `bt_face_cluster_compute_normal_bounding_cone`
456
- has no consumer and no prospect of one.
457
- - **Fixed-size residency slots** `[M §2.5]`. Micron gives every resident patch one slot of
458
- `MICRON_PATCH_SIZE_MAX × 3` vertices with occupancy in a BitSet: no allocator, no
459
- fragmentation, no address migration, at the cost of internal fragmentation proportional to
460
- `1 − fill`. **Declined: pages already are the compaction mechanism.** A page is fixed-size
461
- and carries whole groups; the lever to pull is page waste, by keeping clusters full — which
462
- §1's cluster-count target and repack are for — not by giving up variable-size allocation.
463
-
464
- *(2026-09-12: "by construction" was the original wording and it was not true — the builder
465
- overran `nominal_page_size` whenever a group did not fit, and the format told readers not to
466
- assume the number at all. It is true now, by enforcement rather than by construction:
467
- FORMAT §11.14, the builder refusing a page size no group fits, and the validator behind it.
468
- The conclusion this bullet reaches is unchanged; the premise had to be made real first.)*
469
- - **Shape-aware greedy leaf clustering** `[M §2.8]`. Micron grows leaf patches with a heap
470
- scored on perimeter change, added-neighbour count, and "open face freedom" — how many of a
471
- candidate face's neighbours are about to be stranded
472
- (`Micron/build/clustering/computeOpenFaceFreedom.js`). **Declined: we lean on METIS
473
- heavily**, and replacing it with a bespoke region grower is a large step away from a
474
- partitioner that is tested and shared with the navmesh and graph code. We get the
475
- anti-stranding effect structurally instead, in `absorb_singleton_pieces` — after the
476
- partition rather than before it, which is weaker but far cheaper to own.
477
- - **Spectral partitioning as a primary partitioner** `[M §3.3]`. Dense Laplacian
478
- eigen-decomposition is O(n²) in memory. Kept only as the failure fallback in §2.1.
479
- - **Crack repair instead of crack prevention** `[M §3.2]`. `patch_fill_holes_by_uncollapse`
480
- and `patch_stitch_parent_border` are dead code in Micron — a road already walked. Locked
481
- perimeters won.