@woosh/meep-engine 3.60.3 → 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 (514) 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/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts +3 -0
  9. package/editor/ecs/component/editors/ecs/Trail3DEditor.d.ts.map +1 -1
  10. package/editor/ecs/component/editors/ecs/Trail3DEditor.js +6 -0
  11. package/package.json +6 -2
  12. package/src/engine/EngineHarness.d.ts +3 -9
  13. package/src/engine/EngineHarness.d.ts.map +1 -1
  14. package/src/engine/EngineHarness.js +6 -8
  15. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +9 -6
  16. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  17. package/src/engine/graphics/camera/makeOrbitalCameraController.js +44 -71
  18. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts +64 -0
  19. package/src/engine/graphics/camera/orbital/MomentumAxis.d.ts.map +1 -0
  20. package/src/engine/graphics/camera/orbital/MomentumAxis.js +187 -0
  21. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts +86 -0
  22. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.d.ts.map +1 -0
  23. package/src/engine/graphics/camera/orbital/OrbitalCameraRig.js +319 -0
  24. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts +34 -10
  25. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.d.ts.map +1 -1
  26. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraController.js +116 -30
  27. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -7
  28. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  29. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +1 -37
  30. package/src/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +1 -2
  31. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts +14 -0
  32. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.d.ts.map +1 -0
  33. package/src/engine/graphics/ecs/trail3d/TRAIL3D_FADE_LINEAR.js +22 -0
  34. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts +19 -3
  35. package/src/engine/graphics/ecs/trail3d/Trail3D.d.ts.map +1 -1
  36. package/src/engine/graphics/ecs/trail3d/Trail3D.js +31 -4
  37. package/src/engine/graphics/ecs/trail3d/advance_trail.js +1 -1
  38. package/src/engine/graphics/ecs/water/Water.d.ts +8 -0
  39. package/src/engine/graphics/ecs/water/Water.d.ts.map +1 -1
  40. package/src/engine/graphics/ecs/water/Water.js +8 -0
  41. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts +5 -5
  42. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.d.ts.map +1 -1
  43. package/src/engine/graphics/trail/tube/simulator/TubeXFixedPhysicsSimulator.js +6 -6
  44. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  45. package/src/engine/graphics3/WaterSystem.js +731 -734
  46. package/src/engine/graphics3/decal/DecalTileShader.d.ts +61 -0
  47. package/src/engine/graphics3/decal/DecalTileShader.d.ts.map +1 -0
  48. package/src/engine/graphics3/decal/DecalTileShader.js +138 -0
  49. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts +10 -4
  50. package/src/engine/graphics3/decal/GPUDecalRenderer.d.ts.map +1 -1
  51. package/src/engine/graphics3/decal/GPUDecalRenderer.js +149 -44
  52. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts +25 -0
  53. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.d.ts.map +1 -0
  54. package/src/engine/graphics3/decal/chunk_decal_tile_vertex.js +55 -0
  55. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts +22 -0
  56. package/src/engine/graphics3/decal/graph_gather_decal_tiles.d.ts.map +1 -0
  57. package/src/engine/graphics3/decal/graph_gather_decal_tiles.js +70 -0
  58. package/src/engine/graphics3/decal/shader_decal_composite.d.ts +7 -5
  59. package/src/engine/graphics3/decal/shader_decal_composite.d.ts.map +1 -1
  60. package/src/engine/graphics3/decal/shader_decal_composite.js +48 -44
  61. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts +12 -0
  62. package/src/engine/graphics3/decal/shader_decal_tile_copy.d.ts.map +1 -0
  63. package/src/engine/graphics3/decal/shader_decal_tile_copy.js +78 -0
  64. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts +11 -0
  65. package/src/engine/graphics3/decal/shader_gather_decal_tiles.d.ts.map +1 -0
  66. package/src/engine/graphics3/decal/shader_gather_decal_tiles.js +80 -0
  67. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts +10 -0
  68. package/src/engine/graphics3/trail/trail_tube_write_geometry.d.ts.map +1 -1
  69. package/src/engine/graphics3/trail/trail_tube_write_geometry.js +13 -1
  70. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts +13 -0
  71. package/src/engine/input/devices/mouse/MouseEventButtons.d.ts.map +1 -0
  72. package/src/engine/input/devices/mouse/MouseEventButtons.js +23 -0
  73. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts +4 -0
  74. package/src/engine/input/ecs/controllers/KeyboardCameraController.d.ts.map +1 -1
  75. package/src/engine/input/ecs/controllers/KeyboardCameraController.js +9 -45
  76. package/src/engine/physics/cloth/ecs/cloth_geodesic.d.ts +2 -2
  77. package/src/engine/physics/cloth/ecs/cloth_geodesic.js +2 -2
  78. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  79. package/src/shade/playground/probe_clipmap/README.md +1 -0
  80. package/src/shade/playground/probe_clipmap/index.html +5 -0
  81. package/src/shade/playground/probe_clipmap/main.d.ts.map +1 -1
  82. package/src/shade/playground/probe_clipmap/main.js +16 -2
  83. package/src/shade/playground/vgeo_scene/README.md +1 -1
  84. package/src/shade/playground/water_refraction/README.md +5 -6
  85. package/src/shade/playground/water_refraction/index.html +1 -1
  86. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  87. package/src/shade/renderer/Renderer.js +7 -5
  88. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts +4 -6
  89. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.d.ts.map +1 -1
  90. package/src/shade/renderer/atmosphere/MULTISCATTER_LUT_CONFIG.js +34 -36
  91. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts +1 -1
  92. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.d.ts.map +1 -1
  93. package/src/shade/renderer/atmosphere/chunk_get_multiscatter_values.js +162 -163
  94. package/src/shade/renderer/atmosphere/compute_mie_absorption.d.ts +1 -1
  95. package/src/shade/renderer/atmosphere/compute_mie_absorption.js +1 -1
  96. package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_EARTH.js +1 -1
  97. package/src/shade/renderer/atmosphere/hillaire/ATMOSPHERE_PARAMETERS_STRUCT.js +1 -1
  98. package/src/shade/renderer/atmosphere/shader_multiscatter_lut.js +164 -164
  99. package/src/shade/renderer/atmosphere/shader_sky_irradiance_lut.js +1 -1
  100. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts +40 -80
  101. package/src/shade/renderer/camera/orbital/OrbitalCameraController.d.ts.map +1 -1
  102. package/src/shade/renderer/camera/orbital/OrbitalCameraController.js +197 -147
  103. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  104. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  105. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  106. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  107. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  108. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +4 -4
  109. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +4 -4
  110. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts +2 -1
  111. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.d.ts.map +1 -1
  112. package/src/shade/renderer/geometry/sdf/cards/GPUMeshCardSets.js +18 -1
  113. package/src/shade/renderer/geometry/sdf/cards/mesh_cards_raster_pass_descriptor.js +2 -2
  114. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.d.ts +1 -1
  115. package/src/shade/renderer/geometry/sdf/cards/shader_mesh_cards_histogram.js +1 -1
  116. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts +4 -5
  117. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
  118. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +6 -7
  119. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts +31 -0
  120. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.d.ts.map +1 -0
  121. package/src/shade/renderer/geometry/virtual/proxy/vgeo_to_proxy_mesh.js +343 -0
  122. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts +17 -0
  123. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.d.ts.map +1 -0
  124. package/src/shade/renderer/geometry/virtual/runtime/vgeo_meshlet_metadata_write.js +29 -0
  125. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -1
  126. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +7 -13
  127. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts.map +1 -1
  128. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.js +2 -8
  129. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts +5 -5
  130. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.d.ts.map +1 -1
  131. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSceneHolds.js +10 -10
  132. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts +12 -0
  133. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.d.ts.map +1 -1
  134. package/src/shade/renderer/global_illumination/probe_clipmap/ProbeClipmapSettings.js +22 -0
  135. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts +4 -4
  136. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.d.ts.map +1 -1
  137. package/src/shade/renderer/global_illumination/probe_clipmap/probe/GPUProbeClipmap.js +50 -13
  138. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts +6 -0
  139. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.d.ts.map +1 -1
  140. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP.js +7 -0
  141. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts +13 -0
  142. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.d.ts.map +1 -0
  143. package/src/shade/renderer/global_illumination/probe_clipmap/probe/PROBE_CLIPMAP_RESAMPLING.js +13 -0
  144. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts +17 -0
  145. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.d.ts.map +1 -1
  146. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap.js +20 -0
  147. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts +5 -15
  148. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.d.ts.map +1 -1
  149. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_hit_surface.js +6 -34
  150. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts +18 -0
  151. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.d.ts.map +1 -0
  152. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_resampled.js +42 -0
  153. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.d.ts.map +1 -1
  154. package/src/shade/renderer/global_illumination/probe_clipmap/probe/chunk_probe_clipmap_sample_radiance.js +5 -4
  155. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts +4 -0
  156. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.d.ts.map +1 -1
  157. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_accumulate.js +13 -2
  158. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts +52 -0
  159. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.d.ts.map +1 -0
  160. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_resample.js +250 -0
  161. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.d.ts.map +1 -1
  162. package/src/shade/renderer/global_illumination/probe_clipmap/probe/shader_probe_clipmap_shade.js +1 -7
  163. package/src/shade/renderer/hiz/HZBFootprint.d.ts +14 -0
  164. package/src/shade/renderer/hiz/HZBFootprint.d.ts.map +1 -0
  165. package/src/shade/renderer/hiz/HZBFootprint.js +16 -0
  166. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts +10 -0
  167. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.d.ts.map +1 -0
  168. package/src/shade/renderer/hiz/HZB_FOOTPRINT_X.js +11 -0
  169. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts +10 -0
  170. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.d.ts.map +1 -0
  171. package/src/shade/renderer/hiz/HZB_FOOTPRINT_Y.js +11 -0
  172. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +0 -7
  173. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  174. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +405 -350
  175. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts +12 -0
  176. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.d.ts.map +1 -0
  177. package/src/shade/renderer/hiz/chunk_hzb_footprint_x.js +27 -0
  178. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts +12 -0
  179. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.d.ts.map +1 -0
  180. package/src/shade/renderer/hiz/chunk_hzb_footprint_y.js +27 -0
  181. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts +14 -0
  182. package/src/shade/renderer/hiz/hzb_footprint_texels.d.ts.map +1 -0
  183. package/src/shade/renderer/hiz/hzb_footprint_texels.js +34 -0
  184. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts +0 -5
  185. package/src/shade/renderer/hiz/shader_hzb_reduce_any.d.ts.map +1 -1
  186. package/src/shade/renderer/hiz/shader_hzb_reduce_any.js +37 -57
  187. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts +11 -0
  188. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0.d.ts.map +1 -0
  189. package/src/shade/renderer/hiz/{shader_hzb_reduce_mip0_clip.js → shader_hzb_reduce_mip0.js} +120 -111
  190. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts +39 -0
  191. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.d.ts.map +1 -0
  192. package/src/shade/renderer/light/environment/environment_level_1_blur_plan.js +119 -0
  193. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts +15 -7
  194. package/src/shade/renderer/light/environment/execute_filter_environment_map.d.ts.map +1 -1
  195. package/src/shade/renderer/light/environment/execute_filter_environment_map.js +66 -83
  196. package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
  197. package/src/shade/renderer/light/environment/graph_filter_environment_map.js +15 -6
  198. package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts +3 -0
  199. package/src/shade/renderer/light/environment/shader_blur_environment_map.d.ts.map +1 -0
  200. package/src/shade/renderer/light/environment/shader_blur_environment_map.js +111 -0
  201. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts +3 -0
  202. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.d.ts.map +1 -0
  203. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_blurred.js +128 -0
  204. package/src/shade/renderer/light/environment/shader_downsample_environment_map.d.ts.map +1 -1
  205. package/src/shade/renderer/light/environment/shader_downsample_environment_map.js +0 -1
  206. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  207. package/src/shade/renderer/material/GPUMaterialManager.d.ts +7 -0
  208. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  209. package/src/shade/renderer/material/GPUMaterialManager.js +11 -0
  210. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -12
  211. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  212. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +17 -118
  213. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -3
  214. package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
  215. package/src/shade/renderer/postprocess/nss/NSS.js +1 -3
  216. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts +5 -6
  217. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.d.ts.map +1 -1
  218. package/src/shade/renderer/postprocess/nss/NSS_NETWORK_SCHEMA.js +9 -10
  219. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.d.ts.map +1 -1
  220. package/src/shade/renderer/postprocess/nss/shader_nss_extract_feedback.js +48 -49
  221. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.d.ts.map +1 -1
  222. package/src/shade/renderer/postprocess/nss/shader_nss_postprocess.js +12 -16
  223. package/src/shade/renderer/postprocess/nss/shader_nss_preprocess.js +246 -246
  224. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +1 -2
  225. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -1
  226. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +34 -35
  227. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.d.ts +2 -2
  228. package/src/shade/renderer/shader/chunk/atmosphere/C_MIE_SCATTERING_DECLARATION.js +2 -2
  229. package/src/shade/renderer/shader/chunk/atmosphere/chunk_compute_extinction.js +1 -1
  230. package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.d.ts +1 -1
  231. package/src/shade/renderer/shader/chunk/atmosphere/hillaire/DENSITY_PROFILE_LAYER_STRUCT.js +1 -1
  232. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.d.ts +3 -3
  233. package/src/shade/renderer/shader/chunk/brdf/fresnel/chunk_F_Schlick.js +3 -3
  234. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts +9 -10
  235. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.d.ts.map +1 -1
  236. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_aces_ue_matrices.js +9 -10
  237. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.d.ts +4 -4
  238. package/src/shade/renderer/shader/chunk/color/tonemap/aces/chunk_tonemap_ue_filmic.js +5 -5
  239. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts +1 -2
  240. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.d.ts.map +1 -1
  241. package/src/shade/renderer/shader/chunk/math/chunk_f16_round_stochastic.js +1 -2
  242. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts +14 -1
  243. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.d.ts.map +1 -1
  244. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec2.js +19 -6
  245. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts +10 -1
  246. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.d.ts.map +1 -1
  247. package/src/shade/renderer/shader/chunk/noise/chunk_stbn_sample_vec3.js +14 -6
  248. package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.d.ts +1 -1
  249. package/src/shade/renderer/shader/chunk/random/murmur/chunk_murmur3_vec3u.js +1 -1
  250. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  251. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +7 -2
  252. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts +2 -3
  253. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.d.ts.map +1 -1
  254. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_DYNAMIC_LOD_BIAS.js +7 -8
  255. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.d.ts +1 -1
  256. package/src/shade/renderer/shadow/vsm/DEFAULT_VSM_MAX_LOAD_FACTOR.js +12 -12
  257. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts +2 -2
  258. package/src/shade/renderer/shadow/vsm/VSMPageManager.d.ts.map +1 -1
  259. package/src/shade/renderer/shadow/vsm/VSMPageManager.js +3 -4
  260. package/src/shade/renderer/water/GPUWaterRenderer.d.ts +0 -6
  261. package/src/shade/renderer/water/GPUWaterRenderer.d.ts.map +1 -1
  262. package/src/shade/renderer/water/GPUWaterRenderer.js +12 -17
  263. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts +35 -0
  264. package/src/shade/renderer/water/medium/graph_water_meshlets.d.ts.map +1 -0
  265. package/src/shade/renderer/water/medium/graph_water_meshlets.js +89 -0
  266. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
  267. package/src/shade/renderer/water/shader_water.d.ts.map +1 -1
  268. package/src/shade/renderer/water/shader_water.js +22 -13
  269. package/src/shade/renderer/water/shader_water_avboit.d.ts.map +1 -1
  270. package/src/shade/renderer/water/shader_water_avboit.js +7 -7
  271. package/src/shade/renderer/water/surface/graph_water_tessellation.d.ts +1 -1
  272. package/src/shade/renderer/water/surface/graph_water_tessellation.js +1 -1
  273. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +9 -1
  274. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +0 -19
  275. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
  276. package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +2 -52
  277. package/editor/REWORK_PLAN_2026_08.md +0 -692
  278. package/src/CODEC_LAYOUT_VERIFICATION.md +0 -419
  279. package/src/CORRECTNESS_HUNT_2026_07_24.md +0 -142
  280. package/src/DUPLICATION_AUDIT_2026_08_07/00_core_reuse_surface.md +0 -738
  281. package/src/DUPLICATION_AUDIT_2026_08_07/A_core_math_geom.md +0 -79
  282. package/src/DUPLICATION_AUDIT_2026_08_07/B_core_data.md +0 -93
  283. package/src/DUPLICATION_AUDIT_2026_08_07/C_gfx_render.md +0 -80
  284. package/src/DUPLICATION_AUDIT_2026_08_07/D_gfx_features.md +0 -164
  285. package/src/DUPLICATION_AUDIT_2026_08_07/E_physics.md +0 -69
  286. package/src/DUPLICATION_AUDIT_2026_08_07/F_sound.md +0 -110
  287. package/src/DUPLICATION_AUDIT_2026_08_07/G_net_save.md +0 -129
  288. package/src/DUPLICATION_AUDIT_2026_08_07/H_engine_misc.md +0 -72
  289. package/src/DUPLICATION_AUDIT_2026_08_07/I_generation_view.md +0 -74
  290. package/src/DUPLICATION_AUDIT_2026_08_07/K_crosscutting.md +0 -169
  291. package/src/DUPLICATION_AUDIT_2026_08_07.md +0 -254
  292. package/src/DUPLICATION_AUDIT_REMEDIATION.md +0 -250
  293. package/src/DUPLICATION_REVIEW_2026_10_24.md +0 -1072
  294. package/src/REVIEW_2026_08_06/adv-core.md +0 -511
  295. package/src/REVIEW_2026_08_06/adv-gfx.md +0 -480
  296. package/src/REVIEW_2026_08_06/adv-view.md +0 -446
  297. package/src/REVIEW_2026_08_06/api-core.md +0 -296
  298. package/src/REVIEW_2026_08_06/api-engine.md +0 -195
  299. package/src/REVIEW_2026_08_06/bvh-phys-sound-net.md +0 -507
  300. package/src/REVIEW_2026_08_06/core-binary.md +0 -348
  301. package/src/REVIEW_2026_08_06/core-collection.md +0 -376
  302. package/src/REVIEW_2026_08_06/core-math.md +0 -221
  303. package/src/REVIEW_2026_08_06/core-process.md +0 -266
  304. package/src/REVIEW_2026_08_06/core-science.md +0 -223
  305. package/src/REVIEW_2026_08_06/engine-ai-nav.md +0 -279
  306. package/src/REVIEW_2026_08_06/engine-animation.md +0 -167
  307. package/src/REVIEW_2026_08_06/engine-asset.md +0 -166
  308. package/src/REVIEW_2026_08_06/engine-ecs.md +0 -920
  309. package/src/REVIEW_2026_08_06/engine-input.md +0 -197
  310. package/src/REVIEW_2026_08_06/engine-misc.md +0 -248
  311. package/src/REVIEW_2026_08_06/engine-terrain.md +0 -52
  312. package/src/REVIEW_2026_08_06/generation.md +0 -176
  313. package/src/REVIEW_2026_08_06/geom-2d.md +0 -434
  314. package/src/REVIEW_2026_08_06/geom-3d.md +0 -117
  315. package/src/REVIEW_2026_08_06/gfx-gi.md +0 -143
  316. package/src/REVIEW_2026_08_06/gfx-render.md +0 -740
  317. package/src/REVIEW_2026_08_06/gfx-texture.md +0 -690
  318. package/src/REVIEW_2026_08_06/prior-criticals.md +0 -49
  319. package/src/REVIEW_2026_08_06/prior-highs.md +0 -138
  320. package/src/REVIEW_2026_08_06/test-gaps.md +0 -364
  321. package/src/REVIEW_2026_08_06/view.md +0 -667
  322. package/src/REVIEW_2026_08_06.md +0 -610
  323. package/src/core/binary/lz4/LZ4_HIGH_COMPRESSION_PLAN_2026_09_14.md +0 -542
  324. package/src/core/bvh2/bvh3/NOTES.md +0 -2
  325. package/src/core/bvh8/NOTES.md +0 -63
  326. package/src/core/cache/wtinylfu/benchmark/CacheWTinylfu.bench.md +0 -243
  327. package/src/core/color/COLOR_FIX_PLAN_2026_08_28.md +0 -417
  328. package/src/core/color/COLOR_REVIEW_2026_08_28.md +0 -779
  329. package/src/core/geom/2d/polygon/TRIANGULATION_DESIGN.md +0 -433
  330. package/src/core/geom/3d/atlas/ATLAS_PORT_PLAN.md +0 -486
  331. package/src/core/geom/3d/atlas/BFF_PLAN.md +0 -174
  332. package/src/core/geom/3d/atlas/REVIEW_02.md +0 -392
  333. package/src/core/geom/3d/gjk/GJK_REVIEW_NOTES.md +0 -146
  334. package/src/core/geom/3d/gjk/NOTES.md +0 -8
  335. package/src/core/geom/3d/topology/TOPO_MESH_RETIREMENT_PLAN.md +0 -135
  336. package/src/core/geom/GEOMETRY_REVIEW_002_API_ISSUES.md +0 -381
  337. package/src/core/geom/GEOMETRY_REVIEW_002_NAMING.md +0 -684
  338. package/src/core/geom/MANIFOLD_COMPARISON_2026_08_01.md +0 -554
  339. package/src/core/model/reactive/evaluation/PREDICATE_APPLICABILITY_PROPOSAL.md +0 -446
  340. package/src/engine/REVIEW_2026_07_03.md +0 -211
  341. package/src/engine/asset/loaders/image/IMAGE_CODEC_REVIEW_2026_08_28.md +0 -815
  342. package/src/engine/control/first-person/DESIGN.md +0 -658
  343. package/src/engine/control/first-person/DESIGN_COLLISION.md +0 -382
  344. package/src/engine/control/first-person/DESIGN_EXTENSIONS.md +0 -563
  345. package/src/engine/control/first-person/TODO.md +0 -160
  346. package/src/engine/ecs/ASYNC_SYSTEMS_PROPOSAL.md +0 -531
  347. package/src/engine/ecs/HIERARCHY_REVIEW.md +0 -717
  348. package/src/engine/ecs/async/WORKER_SYSTEM_PLAN.md +0 -262
  349. package/src/engine/ecs/ik/PLAN.md +0 -179
  350. package/src/engine/ecs/storage/binary/SERIALIZATION_COVERAGE.md +0 -272
  351. package/src/engine/ecs/storage/binary/object-v2/DESIGN.md +0 -199
  352. package/src/engine/ecs/transform/TRANSFORM64_MIGRATION_PLAN_2026_09_04.md +0 -521
  353. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +0 -463
  354. package/src/engine/graphics/ecs/camera/pp/NOTES.md +0 -48
  355. package/src/engine/graphics/render/frame_graph/CORRECTNESS_FIXES_2026_09_04.md +0 -659
  356. package/src/engine/graphics/render/frame_graph/FRAME_GRAPH_RECOMMENDATIONS.md +0 -235
  357. package/src/engine/graphics/sh3/path_tracer/REVIEW_001_ACTION_PLAN.md +0 -490
  358. package/src/engine/graphics/texture/sampler/compare/FLIP_IMPLEMENTATION_PLAN.md +0 -466
  359. package/src/engine/input/GAMEPAD_API_PROPOSAL.md +0 -717
  360. package/src/engine/input/ecs/ism/ISM_DESIGN.md +0 -1523
  361. package/src/engine/input/ecs/ism/ISM_IMPLEMENTATION_PLAN.md +0 -627
  362. package/src/engine/navigation/mesh/OVERLAPPING_SURFACES_2026_08_25.md +0 -661
  363. package/src/engine/navigation/mesh/PATHFINDING_PLAN.md +0 -229
  364. package/src/engine/network/CONGESTION_CONTROL.md +0 -87
  365. package/src/engine/network/MESSAGE_TRANSPORT.md +0 -364
  366. package/src/engine/network/SMOOTHNESS_DESIGN.md +0 -400
  367. package/src/engine/physics/cloth/MEASUREMENTS.md +0 -5304
  368. package/src/engine/physics/cloth/PLAN.md +0 -3056
  369. package/src/engine/physics/fluid/GPU_FLUID_CASCADES_PROPOSAL_2026_10_16.md +0 -1382
  370. package/src/engine/physics/fluid/REVIEW_02_PLAN.md +0 -243
  371. package/src/engine/physics/fluid/solver/NOTES.md +0 -46
  372. package/src/engine/sound/SOUND_EMITTER_RETIREMENT_PLAN.md +0 -536
  373. package/src/engine/sound/simulation/IMPLEMENTATION_PLAN.md +0 -432
  374. package/src/engine/sound/simulation/PROBE_PLACEMENT.md +0 -250
  375. package/src/engine/sound/simulation/PROBE_TRANSFER_RESEARCH.md +0 -322
  376. package/src/engine/sound/simulation/REFLECTION_IR_PLAN.md +0 -261
  377. package/src/engine/sound/simulation/VISIBILITY_GRAPH_PLAN.md +0 -121
  378. package/src/engine/sound/sopra/IMPLEMENTATION_PLAN.md +0 -999
  379. package/src/format/image/avif/DECISIONS.md +0 -545
  380. package/src/format/image/jpeg/NOTES.md +0 -3
  381. package/src/format/scene/gltf/MESHOPT_COMPRESSION_PLAN.md +0 -814
  382. package/src/generation/COORDINATES.md +0 -54
  383. package/src/generation/REVIEW_01_ACTION_PLAN.md +0 -628
  384. package/src/generation/foliage/ANNUAL_SHOOTS_BRIEF_2026_10_25.md +0 -220
  385. package/src/generation/foliage/CROWN_FILLING_BRIEF_2026_10_26.md +0 -172
  386. package/src/generation/foliage/CROWN_FULLNESS_BRIEF_2026_10_26.md +0 -86
  387. package/src/generation/foliage/FOLIAGE_DESIGN.md +0 -547
  388. package/src/generation/foliage/FOLIAGE_PLAN.md +0 -198
  389. package/src/generation/foliage/LEAF_ARCHITECTURE_BRIEF_2026_10_26.md +0 -158
  390. package/src/generation/foliage/LOD_REWORK_BRIEF_2026_10_27.md +0 -521
  391. package/src/generation/foliage/REAL_TIME_TREES_BRIEF_2026_10_27.md +0 -86
  392. package/src/generation/foliage/SELF_ORGANIZING_FRAMEWORK_BRIEF_2026_10_26.md +0 -347
  393. package/src/generation/foliage/SHOOT_SYSTEMS_ALL_SPECIES_BRIEF_2026_10_26.md +0 -248
  394. package/src/generation/foliage/SPEEDTREE_COMPARISON_2026_10_24.md +0 -694
  395. package/src/generation/foliage/TWO_FRONT_ENDS_BRIEF_2026_10_28.md +0 -552
  396. package/src/shade/RENDERER_CONTRACT.md +0 -697
  397. package/src/shade/SHADER_F16_PROPOSAL_2026_10_24.md +0 -263
  398. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +0 -569
  399. package/src/shade/device/PASS_ENCODER_PROPOSAL_2026_08_31.md +0 -755
  400. package/src/shade/device/mock/EXTENSION_PROPOSAL_2026_08_22.md +0 -458
  401. package/src/shade/device/mock/IMPLEMENTATION_PLAN.md +0 -255
  402. package/src/shade/device/mock/REVIEW_2026_08_22.md +0 -527
  403. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +0 -894
  404. package/src/shade/device/timing/profile/SGPT_FORMAT.md +0 -345
  405. package/src/shade/renderer/NOTES.md +0 -78
  406. package/src/shade/renderer/PLAN.md +0 -8
  407. package/src/shade/renderer/animation/ADDITIVE_BLENDING_PLAN.md +0 -145
  408. package/src/shade/renderer/animation/SKINNING_FRAME_PLAN.md +0 -357
  409. package/src/shade/renderer/atmosphere/NOTES.md +0 -6
  410. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts +0 -76
  411. package/src/shade/renderer/camera/orbital/MomentumAxis.d.ts.map +0 -1
  412. package/src/shade/renderer/camera/orbital/MomentumAxis.js +0 -164
  413. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts +0 -45
  414. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.d.ts.map +0 -1
  415. package/src/shade/renderer/camera/orbital/OrbitalCameraConfig.js +0 -66
  416. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts +0 -98
  417. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.d.ts.map +0 -1
  418. package/src/shade/renderer/camera/orbital/OrbitalCameraInput.js +0 -184
  419. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts +0 -175
  420. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.d.ts.map +0 -1
  421. package/src/shade/renderer/camera/orbital/OrbitalCameraRig.js +0 -411
  422. package/src/shade/renderer/camera/orbital/README.md +0 -81
  423. package/src/shade/renderer/camera/orbital/index.d.ts +0 -6
  424. package/src/shade/renderer/camera/orbital/index.d.ts.map +0 -1
  425. package/src/shade/renderer/camera/orbital/index.js +0 -9
  426. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +0 -964
  427. package/src/shade/renderer/fluid/GPU_FLUID_OCCUPANCY_AND_SOTA_REVIEW_2026_10_26.md +0 -361
  428. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_BATCH_PLAN.md +0 -619
  429. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +0 -674
  430. package/src/shade/renderer/geometry/sdf/NOTES.md +0 -6
  431. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +0 -540
  432. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +0 -305
  433. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +0 -913
  434. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +0 -787
  435. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +0 -483
  436. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +0 -301
  437. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +0 -448
  438. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +0 -385
  439. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +0 -656
  440. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +0 -308
  441. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +0 -657
  442. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN_2026_09_14.md +0 -551
  443. package/src/shade/renderer/geometry/virtual/runtime/SHADOW_PLAN_2026_09_28.md +0 -536
  444. package/src/shade/renderer/global_illumination/lpv/NOTES.md +0 -3
  445. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_CLIPMAP_PROPOSAL_2026_10_27.md +0 -4225
  446. package/src/shade/renderer/global_illumination/probe_clipmap/PROBE_RAYS_VARIABLE_AND_SORTED_SCOPE_2026_10_30.md +0 -410
  447. package/src/shade/renderer/global_illumination/sharc/NOTES.md +0 -8
  448. package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +0 -1369
  449. package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +0 -202
  450. package/src/shade/renderer/gpu_primitive/indirect/NOTES.md +0 -94
  451. package/src/shade/renderer/hiz/NOTES.md +0 -3
  452. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts +0 -8
  453. package/src/shade/renderer/hiz/shader_hzb_reduce_mip0_clip.d.ts.map +0 -1
  454. package/src/shade/renderer/light/cluster/NOTES.md +0 -11
  455. package/src/shade/renderer/light/cluster/PROPOSAL_ALGORITHM_2026_10_16.md +0 -524
  456. package/src/shade/renderer/light/cluster/PROPOSAL_HIERARCHICAL_2026_10_16.md +0 -1101
  457. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts +0 -13
  458. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.d.ts.map +0 -1
  459. package/src/shade/renderer/light/environment/shader_convolve_reflection_map_from_averages.js +0 -198
  460. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts +0 -24
  461. package/src/shade/renderer/material/resident/shader_resident_texture_means.d.ts.map +0 -1
  462. package/src/shade/renderer/material/resident/shader_resident_texture_means.js +0 -120
  463. package/src/shade/renderer/particles/DESIGN.md +0 -1011
  464. package/src/shade/renderer/particles/EMITTER_GLOBAL_STATE_PLAN_2026_10_29.md +0 -283
  465. package/src/shade/renderer/particles/ORIENTATION_PLAN_2026_10_15.md +0 -453
  466. package/src/shade/renderer/path_tracer/PATH_TRACED_MEDIA_PLAN_2026_10_02.md +0 -520
  467. package/src/shade/renderer/postprocess/bloom/NOTES.md +0 -6
  468. package/src/shade/renderer/postprocess/fsr/NOTES.md +0 -4
  469. package/src/shade/renderer/postprocess/gtao/NOTES.md +0 -6
  470. package/src/shade/renderer/postprocess/nss/NOTES.md +0 -339
  471. package/src/shade/renderer/postprocess/perspective/NOTES.md +0 -3
  472. package/src/shade/renderer/postprocess/sscs/NOTES.md +0 -5
  473. package/src/shade/renderer/postprocess/ssr/NOTES.md +0 -14
  474. package/src/shade/renderer/postprocess/ssr/REPROJECTION_PROPOSAL.md +0 -286
  475. package/src/shade/renderer/postprocess/ssr/prefilter/NOTES.md +0 -39
  476. package/src/shade/renderer/postprocess/taa/NOTES.md +0 -11
  477. package/src/shade/renderer/postprocess/upscale-dither/NOTES.md +0 -78
  478. package/src/shade/renderer/postprocess/vrs/NOTES.md +0 -4
  479. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +0 -1218
  480. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +0 -600
  481. package/src/shade/renderer/restir/reservoir/NOTES.md +0 -6
  482. package/src/shade/renderer/shader/chunk/atmosphere/NOTES.md +0 -6
  483. package/src/shade/renderer/shader/chunk/brdf/FRESNEL_BRIEF_2026_10_16.md +0 -394
  484. package/src/shade/renderer/shader/chunk/color/tonemap/ACES_TONE_MAPPING_BRIEF_2026_10_17.md +0 -160
  485. package/src/shade/renderer/shader/chunk/color/tonemap/HDR_TONE_MAPPING_FORMS_BRIEF_2026_10_16.md +0 -322
  486. package/src/shade/renderer/shader/chunk/color/tonemap/SDR_TONE_MAPPING_BRIEF_2026_10_16.md +0 -247
  487. package/src/shade/renderer/shader/chunk/fsr/NOTES.md +0 -3
  488. package/src/shade/renderer/shader/chunk/fsr/spd/NOTES.md +0 -4
  489. package/src/shade/renderer/shader/chunk/geometry/ray/NOTES.md +0 -4
  490. package/src/shade/renderer/shader/chunk/material/MULTISCATTER.md +0 -159
  491. package/src/shade/renderer/shader/chunk/mboit/NOTES.md +0 -38
  492. package/src/shade/renderer/shader/chunk/spherical_harmonics/l1i/NOTES.md +0 -3
  493. package/src/shade/renderer/shader/chunk/spherical_harmonics/sampling/NOTES.md +0 -8
  494. package/src/shade/renderer/shader/compiler/SHADER_SWITCH_PROPOSAL_2026_10_16.md +0 -932
  495. package/src/shade/renderer/shadow/contact/FINDINGS.md +0 -413
  496. package/src/shade/renderer/shadow/map/csm/NOTES.md +0 -5
  497. package/src/shade/renderer/shadow/ray/NOTES.md +0 -31
  498. package/src/shade/renderer/shadow/sdf/NOTES.md +0 -9
  499. package/src/shade/renderer/shadow/sdf/brixelizer/NOTES.md +0 -5
  500. package/src/shade/renderer/shadow/sdf/cascade/NOTES.md +0 -9
  501. package/src/shade/renderer/shadow/vsm/NOTES.md +0 -12
  502. package/src/shade/renderer/shadow/vsm/PLAN.md +0 -442
  503. package/src/shade/renderer/shadow/vsm/REVIEW_LEDGER.md +0 -43
  504. package/src/shade/renderer/texture/COMPRESSED_TEXTURE_PLAN_2026_09_01.md +0 -2444
  505. package/src/shade/renderer/texture/mipmaps/NOTES.md +0 -4
  506. package/src/shade/renderer/volumetrics/NOTES.md +0 -1329
  507. package/src/shade/renderer/volumetrics/VOLUMETRIC_NOISE_PLAN_2026_10_15.md +0 -336
  508. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts +0 -23
  509. package/src/shade/renderer/water/medium/GPUWaterBodies.d.ts.map +0 -1
  510. package/src/shade/renderer/water/medium/GPUWaterBodies.js +0 -75
  511. package/src/shade/wgsl/NOTES.md +0 -4
  512. package/src/view/elements/progress/segmented/segment_frame_transform.d.ts +0 -15
  513. package/src/view/elements/progress/segmented/segment_frame_transform.d.ts.map +0 -1
  514. package/src/view/elements/progress/segmented/segment_frame_transform.js +0 -69
@@ -1,787 +0,0 @@
1
- # Virtual Geometry — Design
2
-
3
- Status: **builder, container, reader and GPU runtime implemented; the runtime is under review and
4
- not shippable** — see [runtime/REVIEW_LEDGER.md](./runtime/REVIEW_LEDGER.md), which found seven
5
- critical defects including one that breaks §4's no-double-draw guarantee on the ordinary streaming
6
- path. §10's M1 is written below as closed; treat it as *built and not yet trustworthy*. This document pins only what
7
- is certain; everything else is listed explicitly under *Open*. Runtime home is this directory
8
- (`shade/renderer/geometry/virtual/`, mirroring `texture/virtual/`); builder lives with
9
- `core/geom/3d/topology/struct/binary/` and `core/graph/metis/`.
10
-
11
- What exists, in the order a newcomer meets it: `build/` turns a `Geometry` into a cluster DAG,
12
- `format/` writes and validates the `.vgeo` container ([VGEO_FORMAT.md](./VGEO_FORMAT.md)),
13
- `format/read/` reads one back a page at a time with residency and activation, `residency/` owns the
14
- RAM tier's budget, and `runtime/` is everything §7 describes — the selection buffer, the compute cut
15
- pass, the VRAM page pool, the feedback loop, and the seam through which VG instances enter the
16
- meshlet pipeline ([runtime/RUNTIME_PLAN.md](./runtime/RUNTIME_PLAN.md)).
17
-
18
- `shade/playground/vgeo_runtime/` puts an ordinary `Mesh` in an ordinary `Scene` and draws it
19
- entirely from a **GPU** cut through the meshlet pipeline — it proves the engine seam, and
20
- `shade/playground/vgeo_scene/` opens a whole converted set at once, where the claim about a *scene*
21
- is measured. What proves the DAG, the format and the clamp is not a page at all: it is
22
- `runtime/cut/__fixtures__/vgeo_select_cut.js`, a CPU traversal of the same rule that every cut
23
- spec is held to. A third playground once drew that traversal on screen through Shade's direct
24
- geometry path, with its own renderer and its own streaming loop; once the GPU page could show
25
- everything it showed, it was a second runtime for no gain, and it was retired. §10 says which milestone that leaves us
26
- in, and the order the work actually arrived in.
27
-
28
- ## 1. What and why
29
-
30
- Cluster-DAG continuous LOD (Nanite-family): the builder turns a source mesh into a DAG of
31
- fixed-size clusters at halving detail levels; the runtime selects a *cut* through that DAG
32
- each frame so that geometric error stays below a pixel threshold. Rendering cost becomes a
33
- function of resolution, not of source triangle count, and the asset streams — only a
34
- fraction is ever resident in RAM, and a smaller fraction in VRAM.
35
-
36
- VG is an **enabler and scaler**, not a replacement for good art practice. The stress cases
37
- that motivate it: CAD / digital-twin data (inconsistent density, no authored LODs, e.g.
38
- thousands of unique fasteners), large scans, and film-quality assets — up to billions of
39
- source triangles per scene. Instant first pixel on cold load is a hard goal.
40
-
41
- ## 2. Fixed points
42
-
43
- What exists today, with its real contract: **shader-visible record formats and the
44
- algorithmic primitives are the fixed points; the managers and render-graph seams were
45
- built for whole-mesh static residency and need a VG-specific layer on top** — which the
46
- hands-off principle argues for anyway.
47
-
48
- - **Mesh structure**: `BinaryTopology` (BMesh-style, binary pools, float32 coordinates),
49
- with `bt_mesh_validate`, `bt_merge_vertices_by_distance` (float32 weld quantum floor;
50
- its contract requires a short-edge kill/collapse pre-pass — §8), boundary queries.
51
- - **Simplifier**: `bt_mesh_simplify(mesh, target_face_count, restricted_vertices,
52
- protected_faces)` — greedy quadric edge collapse, optimal placement, pinned vertices neither
53
- moved nor removed, protected faces never destroyed. Required extensions (all builder-side;
54
- three of the four have since landed, and the list is kept because the reasoning is what makes
55
- the fourth findable):
56
- 1. ~~report the max/accumulated quadric residual of a pass (§4)~~ **done**. It returns the
57
- object-space distance the surface moved, measured *before* the cleanup pass — see item 2,
58
- where that ordering turned out to be most of the fix;
59
- 2. ~~boundary constraint quadrics~~ **done**. Open and non-manifold edges now carry a
60
- constraint plane through the edge perpendicular to its face, so a border can no
61
- longer retreat at a recorded cost of zero — which it could, and which the round loop
62
- leaned on, since converging a mesh of open shells means eroding them. Measured
63
- before: 400 loose triangles built a root of 25 carrying `self_error = 0`, selected at
64
- every distance and never refined. A second hole went with it: the error was taken
65
- over *surviving* vertices, and geometry deleted outright leaves no survivor to carry
66
- it, so `bt_mesh_simplify` now measures before its cleanup pass and returns an
67
- object-space distance rather than a raw residual. A flat sheet still reports exactly
68
- zero, which is the other half of the contract. The same mechanism later serves UV
69
- seams (§9);
70
- 3. triangle-inversion (flip) rejection — **still open, and the only one of the four that
71
- is**. Pin-forced placement along locked group perimeters is exactly where greedy QEM folds
72
- over. Nothing in `bt_mesh_simplify` tests the sign of a face normal before or after a
73
- collapse, and no build validation catches a fold, so this is a real gap rather than a
74
- latent one;
75
- 4. ~~group-boundary **polyline** preservation~~ **done**. Pinning vertices does not by itself
76
- protect the boundary *edges* between them: a collapse elsewhere in the fan can take away
77
- the only face holding one, and the cleanup pass then removes it. `protected_faces` names
78
- the faces carrying a group's boundary polyline, and `vgeo_build_levels` re-walks every
79
- boundary edge after the call and throws if one is gone — a missing boundary edge is a crack
80
- between two LODs that never closes, so the build stops rather than shipping it.
81
- - **Partitioner**: the ported METIS — `metis_partition_kway` (CSR in, deterministic via
82
- fixed seed, single balance constraint, **unit vertex weights, no contiguity
83
- guarantee**) and `cluster_mesh_metis(result, mesh, patch_size)` (face clustering with a
84
- hard size ceiling, minimized edge cut, dense renumbering). Disconnected parts are
85
- possible and matter (§3).
86
- - **Bounds**: `Miniball` (minimal bounding sphere, iteration-capped). That is the whole list —
87
- **normal cones are not part of this design**. Backface cone culling is declined outright (§9,
88
- plan §5), so `cone_reserved` was reclaimed by `format_version` 5 (format §7), and
89
- `bt_face_cluster_compute_normal_bounding_cone` — the `BinaryTopology` port of the old
90
- `TopoMesh` cone builder — has no consumer and no prospect of one.
91
- - **Renderer formats (the real seam)**: meshlets capped at 128 triangles / 128 vertices
92
- (8-bit local indices); `MESHLET_METADATA_STRUCT`; the draw record
93
- (`MESHLET_DEFINITION_STRUCT` = meshlet index + mesh); bucket pipeline issuing one
94
- `drawIndirect` per material with instanceCount = meshlet count (no multi-draw needed);
95
- HZB two-pass filters keyed on projected bounds against the previous-frame HZB (no
96
- stale-ID interaction with a per-frame-changing cut); attribute encoding for
97
- normals/tangents/colors (oct/packed). **Positions and UV0 are raw float32 today** —
98
- cluster-local position quantization is new work spanning encode, metadata struct, and
99
- every decode shader, not a finished capability.
100
- - **Renderer managers (need a VG layer, not reuse as-is)**: `GPUMeshletManager` pools
101
- metadata+data via OffsetAllocator. **Growth no longer re-packs**: it appends, files the new
102
- tail as free space through `OffsetAllocator#grow`, copies each old buffer into the larger one
103
- at offset 0, and moves no record — so no address is patched and no `changed` fires.
104
- **Compaction still is a CPU-side rebuild** — new buffer pair (transient ~2× VRAM),
105
- CPU-retained metadata mirrors, full re-upload — and it is what closes the holes growth leaves
106
- behind, which under VG's churn is the case that matters rather than the one that got cheaper.
107
- Downstream, meshlet-address migration is still an acknowledged unhandled TODO in
108
- `GPUGeometryManager` (it logs a warning and continues); it is now reachable only through
109
- compaction, which narrows the hole without closing it.
110
- Mesh→meshlet expansion is one shared FrameGraph helper — `graph_expand_meshes_to_meshlets`,
111
- called from four rasterization paths — and it reads a geometry's `meshlets_address` and
112
- `meshlets_count` as one contiguous static range. VG's scattered, dynamic residency does not
113
- fit that (§6, §7 state what replaces them).
114
- - **Readback**: `graph_read_buffer` (MAP_READ scratch from the general buffer pool,
115
- `mapAsync`, ≥1 frame latency). The virtual-texture system is the shipped precedent for
116
- the whole feedback loop and pins its contract: in-flight readbacks capped, feedback
117
- skipped entirely when saturated, occlusion-aware by construction (§6).
118
-
119
- ## 3. Structure: the cluster DAG
120
-
121
- Pinned:
122
-
123
- - **Leaf clusters = meshlets** (≤ 128 triangles), from `cluster_mesh_metis` on the source
124
- mesh, followed by a **connected-components split** (metis parts can be disconnected;
125
- disconnected clusters/groups inflate spheres and force the exact LOD-lockstep
126
- over-rendering this design criticizes fixed-32 grouping for).
127
- - **The round loop** (not a level sweep): hold a *frontier* of every cluster nothing has
128
- merged yet; select a **band** of it by error; partition the band's *cluster adjacency
129
- graph* (`metis_partition_kway`; edge weight = shared boundary size;
130
- connected-components post-pass) into **groups**; lock every vertex on a group's outer
131
- boundary (`restricted_vertices`); `bt_mesh_simplify` the group interior to ~50% of its
132
- triangles; re-split the simplified group into ~half as many new clusters; those are the
133
- *parents* of every cluster in the group. Parents go back into the frontier; clusters the
134
- round did not use stay in it, untouched. Repeat until the frontier is one cluster — the
135
- root. It is a DAG, not a tree: a parent spans a whole group of children.
136
- - **The band is an error octave**, `[e, e · growth]` from the frontier's minimum, widened
137
- until it covers at least a fixed share of the frontier. Every cluster in a group shares
138
- the group's error, so a sweep that merges whatever happens to be on a level writes off
139
- nearly-exact geometry at its cruder neighbour's error, and hands the runtime page bands
140
- spanning an order of magnitude of error when the cut it takes is a *threshold* on error.
141
- The minimum share is not optional: on error spread thinly and continuously — a UV
142
- sphere, poles against equator — an octave alone holds a handful of clusters, and the
143
- round count then scales with the cluster count rather than its logarithm, which no
144
- `level_count` can survive and which a build refuses outright past
145
- `VGeoLevelOptions.max_levels`. Measured on a 98k-triangle sphere against a sweep: growth
146
- 4 cuts clusters-dragged-up by 38% for one extra level; growth 2 cuts it 60% for seven.
147
- On geometry of uniform error the band is the whole frontier and this *is* the sweep.
148
- - **A level is an ordinal, not a generation.** A group's band ordinal is what orders pages;
149
- its clusters may come from any level at or below it, because a cluster no round could use
150
- waits. What the ordinal guarantees is the only thing the format needs: every group sits
151
- strictly below the group holding its parents, so page order by descending level is a
152
- topological order and every child reference points forward in the file.
153
- - **The group is the atomic LOD decision unit.** Locked boundaries plus the group-uniform
154
- cut rule (§4) is what makes independent per-cluster decisions crack-free.
155
- - **Clusters own their payloads.** No vertex sharing across levels or clusters; each
156
- cluster is an independently uploadable, contiguous, quantized meshlet. Duplication
157
- along cluster borders is an accepted cost (cache coherency, page independence).
158
- - **Group size is adaptive.** Fixed 4 (Nanite) starves the simplifier on small groups
159
- (locked perimeter dominates); fixed 32 (Virtuos) locks a large spatial footprint into
160
- LOD lockstep and over-renders under a conservative group error. Proven approach from
161
- the prior WebGL implementation: start small, grow the group when decimation quality or
162
- re-clustering quality is poor.
163
- - **Termination is convergence to a single root, not a stall.** A round is accepted only
164
- if it *strictly* shrinks the frontier; failing that it widens the band, then grows the
165
- group. A build that cannot reach one root **throws** — the runtime starts its cut at the
166
- root, and several parentless clusters is not a shape it can start from. The validator
167
- enforces it from the bytes: exactly one cluster with `parent_error = +∞`, in page 0.
168
- - **A step aims at a cluster count, and a cluster is bounded by two numbers.** What the
169
- simplifier is handed is a triangle target, but a cluster is capped at 128 triangles *and*
170
- 128 vertices, and on geometry that has come apart — seams, small separate parts, the
171
- patches a collapse leaves behind once it has eaten the connective tissue — there are two
172
- or three vertices per triangle rather than a half. Halving the triangles then does not
173
- halve the vertices, the re-cut needs as many clusters as the group consumed, and the step
174
- is worth nothing to the round. So the re-cut is *measured* and the target tightened on
175
- whichever of the two ceilings the attempt broke, until the step strictly shrinks its
176
- group — tightened by the overshoot rather than by the cluster ratio, because what it
177
- costs is fidelity and the overshoot is the least it can pay. Measured on the Zorah
178
- sample: 187 of its 3,163 primitives reached a frontier no grouping could shrink, every
179
- one of them this, and a 188th ran out of `VGeoLevelOptions.max_levels` inching forward a
180
- merge a round — the same refusal to merge, spent on depth rather than on a stall.
181
- - **Islands merge by proximity, once adjacency has nothing left to offer.** A cluster alone
182
- in its connected component can never be coarsened — halving its triangles leaves one
183
- cluster where there was one — so a mesh of separate shells has no root at all under
184
- adjacency alone. Such clusters are bundled along a Morton curve. It is strictly the
185
- fallback: a cluster the partitioner shredded off is folded back into the adjacent piece
186
- it shares the most boundary with, never into the proximity pool, or a lone shell sixty
187
- metres away ends up merged with a fragment of something else.
188
- - **Leaves are re-packed, never merged losslessly.** Where a group is all leaves and fits
189
- inside one cluster, the union *replaces* them rather than becoming their parent. A step
190
- of zero error is a step the cut rule can never take — `project((s, 0)) ≡ 0`, so the
191
- parent never satisfies `project(parent) > τ` and the leaves under it would be stored,
192
- paged, and never selected. This is what makes a scene of small separate parts tractable:
193
- a thousand twelve-triangle bolts need cutting up differently, not simplifying. Above the
194
- leaves the same union is refused, because a coarser cluster cannot simply be deleted —
195
- a subtree hangs off it.
196
- - **The leaf cut does not pack across components; a group's re-split does.** Packing asks
197
- "what is near what", and over the whole mesh the answer from a space-filling curve can be
198
- sixty metres away. A group has already been chosen for proximity, so packing inside one
199
- cannot reach further than the group does.
200
- - **Build validation must check**: group-boundary polylines survive their group's
201
- simplification byte-exact; locked boundaries do not persist across consecutive rounds
202
- (a band's seam with the rest of the frontier is locked for as long as its neighbours
203
- stay out of the band — self-limiting, since the band's own error rises to meet them, but
204
- unproven); the frontier converges to one root; no inverted triangles; error monotonicity
205
- (§4); page-ancestry acyclicity (§5); sampled-Hausdorff spot checks against `r_e` (§4).
206
- - **Content addressing**: the asset's identity is `hash(output bytes)` — that is all
207
- immutability and CDN caching require. Bit-reproducible builds (same input ⇒ same
208
- output) are a *goal* scoped to a pinned toolchain (node major, deterministic seeds,
209
- single-threaded or order-independent reductions), not a correctness requirement:
210
- transcendental libm drift and parallel float reduction order make an unconditional
211
- cross-platform pin unsafe.
212
-
213
- Open: exact group-size policy and its quality signals; simplification execution model
214
- (shared level topology with per-group restricted sets vs extracted group submeshes — the
215
- polyline guarantee above is the constraint either must satisfy); vertex-weighted
216
- partition balance (triangle-count balance instead of cluster-count balance).
217
-
218
- ## 4. Refinement metric
219
-
220
- Distilled from the pixel-error musings
221
- ([share.gemini.google/AfYiNvAB2kt0](https://share.gemini.google/AfYiNvAB2kt0)).
222
-
223
- Pinned:
224
-
225
- - **Error is an upper bound on the object-space distance from the *source* surface** — not an
226
- estimate of it, and not a density heuristic. The threshold is a threshold, so wherever the
227
- number reads low the cut draws a cluster it should have refined and nothing downstream can
228
- tell it did.
229
-
230
- **The quadric cannot be that number, and it is not a tuning question.** Evaluating a carried
231
- quadric at a vertex's position gives the *mean* squared distance to the planes it stands for,
232
- and a mean is never a bound: a vertex that destroyed one feature and stands for a thousand
233
- planes it sits exactly on reports the feature divided by all of them. Measured against the
234
- distance the surface had actually moved, sampled at the source vertices, the quadric reading
235
- came back at `0.34` of it on a closed pyramid, `0.58` on an octahedron, `0.62` on a spike
236
- standing off a plane, and `0.03`–`0.62` across a cone swept in height. Reading it at the
237
- collapses rather than after the pass, and per endpoint rather than summed, narrows *when* it
238
- is diluted and not *that* it is.
239
-
240
- So the quadrics stay, and they decide **where** a collapse puts the vertex, which is what they
241
- are good at. What it **cost** is a separate scalar carried per vertex, composed by the triangle
242
- inequality at every collapse:
243
-
244
- ```
245
- bound[survivor] = max( bound[v1], bound[v2] ) + how far this collapse moved the surface
246
- ```
247
-
248
- Zero at the source, carried across levels beside the quadrics, combined across steps by
249
- `max` — two steps sharing a pinned boundary vertex each produce a valid bound on what that
250
- step folded into it, so it carries the larger. No gains-minus-results, which is a quadric
251
- problem.
252
-
253
- - **The increment is how far the *surface* moved, not how far the vertices went.** A vertex
254
- sliding within its own plane moves and the surface does not, and flat geometry is mostly
255
- that — charging movement makes a flat wall refine forever, which is the one thing this metric
256
- is not allowed to do (the flat-wall test below, that density heuristics were rejected on).
257
- Measured, charging movement reported `15.9` where the surface had moved `0.98`, and `41.6`
258
- on the same shape with a larger plane around it: the old dilution's mirror image, growing
259
- with surface that never moved. So the increment is the distance from each endpoint's former
260
- position to the survivor's one-ring — the geometry now standing where that surface was —
261
- which the sliding case leaves sitting exactly on. With no one-ring left the patch was
262
- consumed whole, there is nothing to measure against, and the movement is the only evidence
263
- there is.
264
-
265
- **Exactly zero, where it is zero.** A point nearer a triangle than the `float32` coordinates
266
- can express is on it, and is reported as `0` rather than as the `1.6e-14` the closest-point
267
- solve returns. Not pedantry: `project((s, 0)) === 0` is an explicit case and the
268
- camera-inside-sphere `+∞` rule is conditioned on `r_e > 0`, so a flat cluster carrying
269
- `1.6e-14` becomes undrawable at close range.
270
-
271
- Measured against the surface's own movement: `1.00` on a closed pyramid and an octahedron,
272
- `1.08` and `1.10` on a spike standing off a 13- and a 25-vertex plane — conservative
273
- everywhere and within ten per cent of tight, and **not growing with the plane around it**.
274
- Still sampled at vertices rather than over triangle interiors, so the sampled-Hausdorff gate
275
- in §3 still earns its place.
276
-
277
- **It costs nothing at a fixed budget.** The numbers rise — the arch primitive's root goes from
278
- `3.786e-2` to `2.031e-1`, which is the old one having been that optimistic — so a given τ
279
- buys a finer cut, and a caller who wants the old cluster count asks for a larger τ. At *equal
280
- drawn cluster count* the picture is the same or better: `-9.3%`, `-6.7%`, `-6.4%`, `-3.1%`
281
- of mean luminance against the old metric over a 16x range of cut depth. The ordering the cut
282
- descends is preserved; what changed is what the number on it means.
283
-
284
- Superseded: "a calibrated measure rather than a proven Hausdorff bound", and the whole of the
285
- carried-quadric reading as the reported error. The carrying was right and stays; reading the
286
- error off it was the mistake.
287
- - **Each pass reports what its own collapses cost**, and a caller carrying the bound across
288
- passes carries it across them too (`vgeo_build_group_step` across its attempts, the frontier
289
- across levels, the child-error floor below as a backstop). A vertex that arrives already
290
- deviating needs no re-reading: it arrives carrying its own bound.
291
-
292
- Superseded: sweeping the surviving vertices after the pass, "before faceless vertices are
293
- cleaned away, so that a patch consumed whole still costs what it cost". The measurement point
294
- was right about the faceless vertex — it is read at the collapse that consumed it now — and
295
- the sweep threw away every sharper reading to get there.
296
-
297
- **Open.** Whether τ converts *accurately* to conservative pixels is still not measured, only
298
- the object-space half of it is. `container_diff` measures how different the picture looks,
299
- which has no unit relationship to a pixel threshold; what would answer it is a readback of
300
- world position at the cut and at LOD 0, projected, worst case in pixels against τ. Until that
301
- exists, "conservative" here means the object-space bound above.
302
- - **A plane weighs the surface behind it, as a multiple of the mesh's mean triangle.** The
303
- mean the error is read as has to be an average over the *surface*; over a plane count it is
304
- an average over how the surface was cut, and a finely tessellated patch outvotes a coarse
305
- one many times its size. Cutting one face in two along its own plane is the clean statement
306
- of the defect — it adds no surface, and the metric heard the same plane twice. Weights are
307
- ratios rather than areas because everything they are stated against is a ratio (the boundary
308
- constraint is three *faces*; the attribute quadric summed with these weighs one per
309
- triangle), and a slot holding square metres would make all of it depend on whether the asset
310
- was authored in millimetres or kilometres. The reported error is a mean, which divides the
311
- weights out again, so normalizing costs nothing there; it buys back every calibration.
312
-
313
- This changes the quadric **matrix**, so it changes the placement solve and not only the
314
- number — a behavioural change to simplification, landed on its own so it can be attributed.
315
- `VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS` was calibrated against the diluted reading and is
316
- **not** re-tuned here; it needs re-tuning against this one, separately.
317
-
318
- - **Composition across levels does not happen**, and that is the point of carrying quadrics.
319
- The measured value is already deviation from the source; a step takes `max(measured, max(e
320
- of child groups))`, where the floor exists only to impose monotonicity — the accumulated
321
- quadric is not forced to rise when the vertex carrying the previous maximum is the one
322
- collapsed away, and a parent claiming to be closer to the source than its own child would
323
- make the cut oscillate.
324
-
325
- Superseded: composing additively (`e_step + max(e of children)`) is what this replaced. The
326
- triangle inequality justifies it as an upper bound and nothing else does — it loosens every
327
- round and scales with the round count rather than with the geometry. Measured on a unit
328
- sphere, the same ninety-triangle root read `0.36` built from 3k source triangles and `13.3`
329
- built from 24k; carrying quadrics it reads `0.0315` either way, which is the same
330
- approximation reporting the same number.
331
- - **Units guard**: if vertex placement ever moves to higher-dimensional quadrics
332
- (position + UV + normal), errors are still evaluated by plugging the resulting
333
- *position* into the pure 3-D geometric quadric ("dual bookkeeping": solve in high-D,
334
- evaluate in 3-D), so `r_e` stays in meters. Never take a root of a mixed-unit residual.
335
- - **Per-cluster record stores two (sphere, error) pairs**, from *group* steps so all
336
- siblings compute identical values:
337
- - `self`: the step that created this cluster. Leaves store (own cluster sphere, 0).
338
- - `parent`: the step that consumed it (roots: +∞).
339
- - **Monotonicity is enforced at build**: `parent.error ≥ self.error` and `parent.sphere ⊇
340
- self.sphere`, transitively. Without it the cut oscillates and flat evaluation is invalid.
341
- - **Cut rule with residency clamp** — the per-cluster form, which the traversal reaches the
342
- same answer as by stopping its descent rather than by evaluating it everywhere: draw a
343
- cluster iff
344
-
345
- `own group selectable AND project(parent) > τ AND (project(self) ≤ τ OR child group
346
- non-selectable)`
347
-
348
- The first term is not decoration: a group that is resident but not yet selectable satisfies
349
- the rest of the rule while the coarser stand-in still covers the same surface, and both are
350
- drawn a few microns apart — which reads as corrupt geometry rather than as a missing page.
351
- The clamp is what makes "no holes, ever" (§6) true: without it, when the τ-selected
352
- level lies below the streaming frontier, *no* resident cluster on that path satisfies
353
- the pure rule and the guaranteed stand-in is never selected. The child-group
354
- *selectable* bit flips **group-atomically** (a group never spans pages — §5 — and
355
- becomes selectable only through activation — §6 — so page install/evict flips whole
356
- groups; sibling disagreement would crack the frontier).
357
- - **Projection**: `err_px = r_e · screen_height_px / (2 · tan(fov_y / 2) · d)` with `d` =
358
- distance from camera to the *closest point* of the pair's sphere (conservative).
359
- `project((s, 0)) ≡ 0` unconditionally; the camera-inside-sphere ⇒ +∞ convention applies
360
- only when `r_e > 0` (otherwise leaves become undrawable exactly at close range).
361
- - **The cut is a traversal, and there is one per view.**
362
-
363
- *Traversal.* Descent from the root, testing a node or cluster and stopping when it is fine
364
- enough, rather than a flat pass evaluating every resident cluster. Flat evaluation is
365
- data-parallel and simple, and it does not scale: its cost is the resident set, which at the
366
- scale this design targets is millions of clusters every frame, most of them off-screen. A
367
- traversal costs the cut. It also gets two things for free that the flat rule pays for
368
- separately — a missing page simply stops the descent, which *is* the residency clamp, and
369
- culling at an interior node discards a whole region rather than one cluster. WebGPU guarantees no
370
- forward progress across workgroups, so the persistent-thread answer (nvpro, vk_lod_clusters
371
- comparison §2.4) is unbounded waiting and is not available; a wave redistributes its own
372
- production among its lanes instead, and the descent's depth runs inside one dispatch. The
373
- hierarchy it descends exists at two granularities. Pages form a rooted, acyclic,
374
- fully-reachable DAG (§11.3–11.4 of the format), measurably
375
- many-parented on any asset large enough to have more than one page per band, and every
376
- child-table entry and page header now carries a **traversal metric** so a page can be tested
377
- *before* it is fetched. Below page granularity, each page carries a tree over its own groups
378
- (format §6.1–6.2). Cost, measured: 0.3%–1.6% of asset size.
379
-
380
- *What exists.* `runtime/cut/__fixtures__/vgeo_select_cut.js` is this rule on the CPU, against a
381
- partially resident container: descend from the root's parentless groups, refine where
382
- `project(parent) > τ`, take the cluster where it does not, and stop where the child group is
383
- not selectable — recording the want that would unblock it. It is the reference the GPU pass
384
- has to agree with, and it is where the clamp was found to need *selectable* rather than
385
- *resident* (§6). What it does **not** do is use either accelerator this section just
386
- described: it descends group by group, and reads neither the child-table traversal metric nor
387
- the page node tree. Both are written by the builder and checked by the validator, and nothing
388
- reads either — the largest piece of built-and-unused machinery in the system, and the cut pass
389
- is what it was built for.
390
-
391
- *Per view.* **Virtual geometry is view-dependent by construction.** A cut is selected at one
392
- camera's distances and pixel budget; it is the wrong cut for a shadow view, whose light may
393
- sit a hundred metres behind the camera, and wrong again for each cascade. What is shared
394
- between views is the *setup* — the traversal kernel, the residency state, the resident page
395
- working set — not the result. Where a view does not justify a cut at all, it should take a
396
- proxy mesh instead (plan §3.2, §3.3).
397
-
398
- Superseded: this section previously claimed the cut was "view-agnostic in *coverage*" and
399
- that shadow views could reuse the camera's. They cannot, and the claim is withdrawn.
400
- - **Threshold `τ` defaults to 1 px and is a runtime dial** — the single quality/perf
401
- lever. Empirically (prior implementation), pops stay invisible well above 1 px:
402
- headroom for weak GPUs and dynamic resolution integration. **Read that number with care**:
403
- the prior implementation shipped `1 / face_count` as its group error, a density heuristic
404
- this section rejects below, so the observation is about a threshold dial under a different
405
- metric and is not calibration for `r_e`.
406
- - **Rejected**: pixels-per-triangle density heuristics — they fail the flat-wall test
407
- (flat *closed-interior* geometry is exact at any triangle size; `r_e = 0` encodes that;
408
- the qualifier matters — at *open boundaries* zero-residual erosion is real, which is
409
- why §2 requires boundary constraint quadrics). And geomorphing — double residency and
410
- bandwidth for a transition a tight metric already makes invisible; Epic disabled
411
- theirs too.
412
-
413
- Open: separate UV/attribute error term (CAD rarely; textured game art maybe — §9); τ
414
- semantics under dynamic resolution scaling (native vs render pixels).
415
-
416
- ## 5. Binary format
417
-
418
- A new container, designed for streaming: `.vgeo`. Requirements are pinned here; the
419
- concrete layout is specified in [VGEO_FORMAT.md](./VGEO_FORMAT.md).
420
-
421
- Pinned:
422
-
423
- - **Scale**: billions of source triangles per asset ⇒ 64-bit byte offsets everywhere.
424
- Little-endian, versioned, magic-tagged header. All counts below are **lower bounds**
425
- (cluster fill < 128 under the hard ceiling; per-level reduction < 50% happens): 1 G
426
- tris ⇒ ≥ ~8 M leaves, ≥ ~16 M DAG clusters, tens of GB of payload at today's raw-f32
427
- positions (~4 KB per 128/128 cluster). Budgets and page sizing are set from measured
428
- fill in M2, never from these nominals.
429
- - **Immutable and content-addressed** (`hash(output bytes)` in header and filename):
430
- perfect HTTP caching, CDN-friendly, no invalidation protocol.
431
- - **Fetch unit = size-bounded page** (64 KiB, `VGEO_DEFAULT_PAGE_SIZE` — **one** page size, against
432
- Nanite's two, traded off between hiding fetch latency and both response time and VRAM per
433
- resident page on a low-end device; this section's "order hundreds of KB" was a first-principles
434
- guess and is superseded, see the constant's own note), fetched by
435
- HTTP **single-range** requests and **independently decodable**: a group never spans
436
- pages; a page carries whole groups with payloads in (near-)upload-ready encoded form.
437
- - **Pages carry the DAG, fetchably**: each cluster record stores its two (sphere, error)
438
- pairs (§4) and a child reference (leaves: sentinel), and the page carries a
439
- deduplicated **child table** with each referenced child page's exact byte range.
440
- Page-to-page traversal is self-sufficient: an installed page fetches everything one
441
- level down without consulting any global index. Wants (§6) are page indices resolved
442
- entirely from the want-emitting page.
443
- - **The hierarchy itself is paged.** Per-cluster metadata alone reaches GBs at the stated
444
- scale and cannot be unconditionally resident. Only the root page — the topmost
445
- level(s), cluster records and payloads together, at minimum a complete drawable cut —
446
- is always resident; deeper pages stream on demand.
447
- - **Time-to-first-pixel is O(1) by construction**: the fixed-size header directly carries
448
- `(offset, size)` of the root page — first pixel needs header + root page only. The page
449
- directory is **auxiliary**: fully derivable from the pages themselves, range-fetched
450
- only for tooling and special tasks, never a runtime prerequisite (at billions scale it
451
- is MBs).
452
- - **Page-ancestry is acyclic by construction**: child references point strictly forward
453
- in the file (coarse-first order is topological); non-root pages hold clusters of a
454
- single level band. Build-validated.
455
- - **Compression must not break random access.** Transparent HTTP compression does not
456
- compose with range requests, so compression is per-page *inside* the container. Three codecs
457
- are defined and all three shipped — `raw`, a bare LZ4 block, and a bare DEFLATE stream through
458
- the platform's own zlib — chosen **per frame** rather than per file, so a page that would not
459
- shrink is stored verbatim and says so. Measured, LZ4 is worth 1.12x–1.18x on the Stanford
460
- models and deflate about seven points more for four and a half times the decode; deflate is what
461
- the builder writes, because a build happens once and a fetch happens forever. The reason neither
462
- is worth more is the reason to stop here: the payload is float32 positions and packed attribute
463
- words, and it does not compress. Quantization (§2) is the lever, not the codec.
464
- - **Coarse-first layout**: pages ordered so a plain prefix read of the file yields a
465
- complete coarse asset — progressive load without a feedback loop.
466
-
467
- Open: a page codec that knows what it is compressing — meshopt-style over the payload rather
468
- than an LZ matcher over its bytes — if quantization leaves one worth having;
469
- per-attribute quantization widths (cluster-local grids); relationship to
470
- `MeshletGeometrySerializationAdapter` (extend vs parallel).
471
-
472
- ## 6. Streaming and residency
473
-
474
- Pinned:
475
-
476
- - **Three tiers with independent budgets**: CDN/network (whole asset) ⊇ RAM (page cache)
477
- ⊇ VRAM (resident cluster pool). Residency is a small fraction of the asset in RAM and a
478
- smaller one in VRAM. Instances of the same asset share pages and residency.
479
- - **The VRAM pool's hard ceiling is the adapter's `maxStorageBufferBindingSize`**, not
480
- free configuration — pools bind as single storage buffers (spec default 128 MiB; Shade
481
- requests adapter max, but low-end adapters stay low). Growth must clamp against device
482
- limits with a defined failure policy; the low-limit fallback is a smaller pool + higher τ.
483
- Multi-buffer sharding is explicit future work.
484
- `GPUMeshletManager.grow` clamps now, having not before — but against **`maxBufferSize`**,
485
- which is the wrong limit for an arena that binds as one storage buffer. Spec defaults are
486
- 256 MiB and 128 MiB respectively, so a buffer between the two is creatable and not bindable.
487
- `GPUDatabase` clamps against the binding limit; `GPUMeshletManager` and
488
- `GPUGeometrySDFManager` clamp against `maxBufferSize`. A VG pool has to pick deliberately,
489
- and the reason this is worth stating rather than assuming is that `createBuffer` answers an
490
- over-limit size with an *invalid buffer* rather than an exception — the failure is silent at
491
- the point it happens and loud several frames later.
492
- - **The VRAM paging mechanism is new work**, not `GPUMeshletManager` as-is (§2): v1
493
- intent is **fixed-size page-slot allocation** (no compaction, no CPU metadata mirror,
494
- no transient 2× rebuild), plus propagation of address migration to every consumer —
495
- the existing `GPUGeometryManager` TODO becomes load-bearing under churn.
496
-
497
- What that rests on is the container, and it now holds: FORMAT §11.14 makes
498
- `nominal_page_size` a bound no page exceeds, announced by a feature bit a pool can require.
499
- Before that it was an advisory number the builder itself broke — 13 of 40 pages over it on a
500
- measured build — and a fixed stride sized against it would have been a slot overrun. A pool
501
- taking the guarantee is entitled to one slot size and nothing else: it refuses a container
502
- built at a different page size rather than carrying a size-class table for it.
503
- - **Activation invariant (closure without dependency metadata)**: a group is
504
- *selectable* iff **all** of its parent-cluster references are live — each referencing
505
- cluster's page installed and that cluster's own group selectable (per-group reference
506
- counter against the format's `parent_cluster_count`; root-level groups have count 0
507
- and activate at install; activation cascades and flips whole groups). A selectable
508
- cluster's entire ancestor cone is therefore installed and selectable — the closure
509
- the cut rule needs, carried by a counter instead of dependency lists. The runtime
510
- keeps a reverse-reference index for not-yet-installed pages (the same structure that
511
- carries wants); a want targeting an already-installed page resolves as an activation
512
- recheck, never a fetch. With the §4 clamped rule, any miss (or resident-but-inactive
513
- group) has a coarser stand-in that *is selected* — streaming latency degrades
514
- quality, never correctness. No holes, no double-draw, in any arrival order.
515
- **Implemented**, in `VGeoContainerReader`: a live-reference counter per resident group, a
516
- reverse index for pages not yet installed, cascades on both install and evict, and
517
- `is_selectable(page, group)`. The double-draw half of that guarantee is not theoretical —
518
- clamping on residency instead drew two levels over the same surface on 355 of 576 camera
519
- positions of a 19-page bunny, up to 10.7% of drawn triangles in a doubly-covered region, and
520
- it reads as corrupt geometry rather than as a missing page, which is what makes it hard to
521
- attribute. 21 of that asset's 362 non-root groups have parents in more than one page, which
522
- is all it takes.
523
- - **Feedback carries touches as well as wants.** Wants = pages whose refinement was
524
- blocked; touches = pages actually selected this frame. Eviction is LRU on
525
- **last-touched** with a protect window ≥ feedback latency — LRU on last-*wanted* would
526
- freeze stamps at fetch time and evict exactly what is on screen (the classic
527
- fallback-chain bug; the VT system already keys on touch for this reason).
528
- **Implemented**, in `virtual/residency/`: `VGeoResidencyManager` holds the RAM tier's byte budget
529
- across every registered reader, `touch(reader, page)` takes the stamp, `advance_frame()` moves the
530
- protect window, and `evict_to_budget()` drops on the format §10 rule ordered by coldest touch.
531
- It is a sibling of `format/` and not part of it, because a container cannot answer a question
532
- about a scene. The reader stayed mechanics: it reports `resident_bytes` and enforces nothing, and
533
- the manager is written entirely against its public surface.
534
- The parenthetical above is load-bearing and should be followed rather than taken on trust:
535
- `VTPhysicalCache` is the working implementation of this same rule, with a `#last_touch`
536
- `Uint32Array` per slot, `touch`/`last_touch`, a lock flag for slots that may never be evicted,
537
- and `find_eviction_candidate(current_frame, protect_window)` whose doc gives the window the
538
- identical justification. The similarity is therefore convergent *by instruction* — this bullet
539
- told the implementation to key on touch because VT does — and the only real difference is that
540
- VT budgets a count of fixed-size slots, which is what lets it pick by linear scan at ≤ ~1k
541
- slots. Budgeting **bytes** across **several** readers, and admitting a page against that budget
542
- before the fetch out of the `decoded_size` its parent already carries, is the part with no
543
- precedent in this tree; so are per-page retry-into-quarantine and per-fetch `AbortSignal`.
544
- - **The fetch cap has to be shared, and per-reader caps do not compose.**
545
- `VGeoReadOptions.max_concurrent_fetches` bounds one reader, which is all a reader can see: four
546
- readers at a cap of four each were measured issuing sixteen concurrent range requests, eight at
547
- the default six issuing forty-eight. `VGeoResidencyManager` holds one total across every reader
548
- as one priority queue, so a want's priority — pixels of projected error, comparable across assets
549
- by construction — decides globally rather than within whichever reader asked first.
550
- A shared cap is not itself a new idea here: `AssetManager.load_concurrency` bounds concurrent
551
- top-level loads for the same reason and orders its queue through a negated priority into a
552
- `BinaryHeap`, and `VirtualTextureManager` bounds `max_concurrent_loads` at 32. Writing that
553
- negation a second time is what moved the queue out of this directory: it is
554
- `core/process/ConcurrencyGate.js` now — a free-standing "run at most N of these at a time,
555
- highest priority first" — and the `VGeoFetchScheduler` that shipped first was folded into it.
556
- The other callers kept their own queues, each recording why where its cap is declared. What none
557
- of them can do is compare two containers' wants — VT sorts its queue coarse-mip first, a proxy
558
- that only orders within one texture, and `AssetRequestScope.final_priority` is hierarchical
559
- rather than a common scale. The cut hands this one, so the global order is the half that is
560
- actually new.
561
- - **Wants are occlusion-aware in v1** (HZB test in the want path, or previous-frame
562
- feedback à la VT — which is occlusion-aware by construction). Frustum-only wants plus
563
- error-priority would page in and pin entire occluded interiors, breaking both budgets.
564
- - **The wants loop tolerates its transport**: multi-frame `mapAsync` latency, a small
565
- in-flight readback cap, and frames where feedback is skipped entirely (saturation) —
566
- the VT contract. Priority = projected error reduction; requests dedup across frames
567
- and instances; scoring/hysteresis details open.
568
- - **In-flight protection**: a fetch pins the page it was discovered through until the
569
- child installs — known at queue time, purely local. Arrivals verify the
570
- identity-seeded frame checksum; activation makes any arrival order safe beyond that.
571
- - **Network**: batched = adjacent wanted pages coalesced into **contiguous single-range**
572
- requests over H2/H3 multiplexing (multipart ranges fail on real CDNs — S3 returns 200
573
- + full body); responses are verified `206`, with immediate abort on `200`.
574
- - **Disk tier is an explicit per-page store or nothing.** The implicit browser HTTP cache
575
- cannot serve as one: the Cache API rejects 206 responses and browsers do not usefully
576
- cache/serve partial ranges at this scale. v1 may ship CDN + RAM only; the page store
577
- (OPFS vs IndexedDB) is the open disk-tier design.
578
- - **Uploads are budgeted per frame** (bytes/frame cap) into the VG residency pool; the
579
- root page is pinned; within each tier, a page is evictable only while none of its
580
- child pages are resident in that tier (checked against its own child table —
581
- leaves-first order emerges from the rule).
582
-
583
- Open: page store API choice; prefetch heuristics (camera velocity); priority scoring and hysteresis
584
- constants; the in-flight pin, which is now blocked on a `VGeoPageWant` that names the page it was
585
- discovered *through* rather than on nobody owning the budget (format §10 — it is an efficiency item,
586
- because activation is safe at every arrival order). RAM eviction is no longer open; the numbers in
587
- `VGeoResidencyOptions` are defaults to be overridden by whoever knows the device, not measurements.
588
-
589
- ## 7. Runtime pipeline (per frame)
590
-
591
- 1. Instance-level culling — existing.
592
- 2. **VG cut pass (new, compute)**: a §4 traversal, one per view. It descends the page DAG
593
- and, inside a page, that page's node tree, from a queue seeded with the root cluster of
594
- each visible instance; a node or cluster fine enough for τ terminates the descent, and so
595
- does one whose child group is not selectable — which emits a want. It emits meshlet draw
596
- records (existing record type) and wants/touches. VG carries geometry only — materials are
597
- orthogonal and engine-side: a VG instance binds one geometry to one material through its
598
- mesh exactly like any other geometry (a multi-material source splits into one geometry per
599
- material upstream of the builder), so the mesh-keyed material buckets downstream stay
600
- unchanged.
601
-
602
- What it reads is the **page arena** — one fixed-stride slot per resident page, holding that
603
- page's header, node rows, group rows and cluster rows — and the per-geometry lookup that
604
- resolves a child page index to a slot (RUNTIME_PLAN §3.1). There is no selection buffer and
605
- no page table: a page's header is the first words of its own slot, and the two (sphere,
606
- error) pairs are in its cluster rows.
607
-
608
- Superseded: this step previously specified a **flat** evaluation of the rule over every
609
- row of that buffer, at a cost of `O(Σ over visible instances of resident(geometry))`. §4
610
- replaced it, and the reason is worth keeping: that cost is the resident set rather than the
611
- cut, and it is the resident set that grows without bound at the scale this design targets.
612
- The buffer survives the change; the sweep over it does not.
613
- 3. Existing pipeline: HZB meshlet filtering (two-pass, per view — this is where frustum
614
- and occlusion happen), material bucket sort, indirect draws, G-buffer, deferred
615
- shading. The *record formats* flow unchanged; the *injection point* is new work — the
616
- current mesh→meshlet expansion (`graph_expand_meshes_to_meshlets`, one helper shared by
617
- four rasterization paths) reads one contiguous static meshlet range per geometry, so VG
618
- instances enter through their own expansion step. M0 was where that seam was to be fixed
619
- and it was not (§10); it is the first thing M1 has to do.
620
-
621
- The traversal's GPU shape is settled and built: one indirect dispatch per spill generation, the
622
- descent's depth inside the kernel, work redistributed across a subgroup's lanes by prefix sum. A
623
- step costs about 0.5 µs whatever the wave holds, so a light asset pays for the shape of its descent
624
- rather than for its geometry, while a container with real work in the frontier pays mostly for the
625
- work. How many dispatches a cut needs is a queue-drain quantity rather than depth divided by the
626
- step count: a wave keeps `subgroup_size` children and spills the rest to the next dispatch.
627
- CUT_PLAN_2026_09_14 proposes to stop re-walking from the root by keeping the cut between frames.
628
-
629
- Open: shadow views (v1 takes the one cut per view of §4, sharing setup rather than results, and a
630
- proxy where a view does not justify a cut); DRS τ semantics.
631
-
632
- Settled since: **flat evaluation versus a hierarchy is no longer a measurement to take.** §4
633
- pins the traversal, and the hierarchy it descends is already written into every built asset
634
- (plan §2.3). The prior-implementation data that made flat evaluation look adequate — selection
635
- cheap, triangle throughput and screen-size uniformity dominant — was taken at that
636
- implementation's scale, and is not evidence about millions of resident clusters.
637
-
638
- ## 8. Builder pipeline (offline)
639
-
640
- Pinned:
641
-
642
- - Stages: load → short-edge pre-pass (`bt_mesh_kill_short_edges` on soups / edge collapse
643
- on closed surfaces — required by the weld contract) → weld
644
- (`bt_merge_vertices_by_distance`; float32 quantum floor constrains tolerance) →
645
- `bt_mesh_validate` → level-0 clustering (+ CC split) → the §3 round loop → per-cluster
646
- encode → page assignment (level-banded, DAG + spatial locality, acyclicity check) →
647
- container emit. The encode is the miniball sphere, the AABB, and `SHADE_MESHLET_V1`
648
- attributes at their shipped widths (format §8): there is no cone field because cone culling
649
- is declined (§9), and positions are raw float32 because quantization is a `payload_encoding`
650
- that does not exist yet.
651
- - **The weld tolerance is scale-relative**, a fraction of each geometry's bounding-box
652
- diagonal (`bt_mesh_relative_merge_distance`), resolved once off the source's bounds
653
- before any pass has removed anything. A conversion is many geometries at many sizes and
654
- no single distance is right for all of them: a fixed `1e-4` leaves an 18-unit dragon
655
- intact and tears 1,061 faces out of the same dragon at 1/100th scale. The ratio's own
656
- interval is narrow — floored by the float32 quantum (`~1e-7` of the diagonal), capped
657
- well under the finest feature a quantized source can carry (`6.1e-5` at `gltfpack`'s
658
- 14-bit default) — so `1e-5` is bracketed rather than tuned, and is not a knob callers
659
- are expected to move.
660
- - **Portable core**: the builder core is environment-agnostic JS — typed arrays and
661
- `BinaryTopology` only, no fs/network/process APIs inside it. I/O and parallelism enter
662
- through injected seams, and two harnesses are first-class: **node** (CLI, file streams,
663
- worker_threads) and **browser** (user-picked handles / OPFS, Web Workers — injected
664
- worker spawn, per the existing WorkerSystem precedent). The bounded-memory invariant
665
- below is what makes the browser harness viable: the RAM budget is a configuration
666
- input, and a browser build simply runs with a tighter budget and smaller islands.
667
- - **Bounded memory**: the builder never requires the full source mesh in RAM. The level
668
- loop needs the current level plus one group neighborhood. Level 0 of a billions-scale
669
- mesh does not fit one `BinaryTopology`, so the source is pre-split into spatial
670
- islands. **Island seams are locked while islands are separate, so interiors halve but
671
- seams do not** — islands therefore merge **hierarchically, pairwise**, dissolving the
672
- shared seam at each merge (never "all at once at the top"), and the split heuristic
673
- carries an explicit seam-size budget. Seams must be gone well before the root.
674
- - Deterministic seeds; asset identity = output hash (§3).
675
-
676
- **One source container, many assets.** A scene file is not a geometry: glTF holds meshes of
677
- primitives, placed by nodes. The unit the builder takes is the *primitive* — the smallest thing the
678
- container calls a geometry, and already one material's worth — so a container yields one `.vgeo` per
679
- primitive, each in object space with the node transforms left to the instances that carry them. The
680
- per-material split §7 asks for therefore costs nothing on this path.
681
-
682
- Open: island split heuristic and seam-quality validation; parallelism (WorkerSystem) vs
683
- the reproducibility goal; browser-harness plumbing (OPFS vs user-picked file handles;
684
- per-allocation ArrayBuffer ceilings feeding island sizing); the per-material split's
685
- boundary handling (adjacent per-material geometries coarsen independently, like any two
686
- neighboring assets); instance deduplication beyond what content addressing gives for free
687
- (identical geometry hashes to one file; *near*-identical fasteners still do not, and
688
- canonicalizing them is a preprocessor's job).
689
-
690
- ## 9. Non-goals (v1)
691
-
692
- - **Backface normal-cone culling — declined outright, not deferred.** The two-pass HZB already
693
- rejects what a cone would, on evidence rather than on a bound; see plan §5 for the mechanism
694
- and the reports behind it. `cone_reserved` was a zeroed reserved field until `format_version` 5
695
- spent it on `subtree_aabb` (format §7). This is the one entry here that is not a "v1" qualifier.
696
- - Skinned / morphing / WPO meshes through the VG path (static geometry only).
697
- - **Non-opaque VG instances.** The cut runs beside the mesh→meshlet expansion of the *opaque*
698
- rasterization paths, and the transparent and alpha-tested bucket path
699
- (`prepare_meshlet_draw_commands_by_material`) has no seam: it is handed a scene and an optional
700
- camera resource rather than a view context, where a cut needs the camera's uniform buffer to put
701
- each instance into object space. A VG instance with a non-opaque material therefore expands to
702
- zero meshlets and draws nothing at all. `GPUSceneRows.attach_mesh` says so out loud when one is
703
- attached, because this bullet is the authority for it and did not exist while the code was citing
704
- it.
705
- - Compute/software rasterization — WebGPU lacks portable 64-bit atomics; the hardware
706
- raster path through the existing pipeline is the only raster path. Deferred G-buffer
707
- shading already bounds the overshading cost of small triangles.
708
- - Ray tracing over the VG cut (`blas_from_meshlets` exists for static meshes; VG×RT is
709
- future work).
710
- - *Sharing* one cut between views. §4 pins a cut per view and a proxy mesh where a view does
711
- not justify one (plan §3.2, §3.3); what v1 does not attempt is a single cut made to serve the
712
- camera and a shadow cascade at once, which was this document's earlier position and is wrong.
713
- - Attribute-quadric placement and UV-seam constraint planes (the boundary-plane-penalty
714
- mechanism §2 requires for open boundaries is the same one UV seams will use; v1
715
- targets position/normal-fidelity content).
716
- - Multi-buffer VRAM sharding beyond the single-binding limit.
717
- - Editor tooling beyond a playground prototype.
718
-
719
- ## 10. Milestones
720
-
721
- These have not been taken in order. The format and the RAM-tier pager were built before the
722
- GPU cut, because the container is what the builder had to produce anyway and a reader is what
723
- proves it; the consequence is that M0's one *engine*-facing criterion — the expansion seam —
724
- is the piece still missing, and M1 is where the work now starts.
725
-
726
- - **M0 — DAG proof** — *the DAG half is done; the engine half is not*: builder (in-memory, no
727
- format) → flat CPU-side cut → existing meshlet pipeline draws it in a playground; the VG
728
- expansion seam is fixed here.
729
- Acceptance: crack-free under τ sweep and flythrough (incl. group-boundary polyline and
730
- flip validation); drawn-triangle count tracks resolution, not source size; §3 build
731
- validations green.
732
- **Done**: the builder; a CPU cut (`runtime/cut/__fixtures__/vgeo_select_cut.js`) that is a traversal
733
- rather than the flat pass planned here, and that runs against a partially resident container,
734
- which was M2's job; τ-sweep watertightness as a spec (`build/vgeo_cut.spec.js` counts edges
735
- used by one triangle instead of two, at a spread of thresholds, and wants zero); the
736
- group-boundary polyline guarantee, enforced by the builder rather than merely validated.
737
- **Done since**: the expansion seam. `runtime/graph_vgeo_expand_instances_to_meshlets` runs beside
738
- `graph_expand_meshes_to_meshlets` at each of the three opaque call sites and appends into the same
739
- collection, so a VG instance enters through a VG-shaped door and everything past the expansion is
740
- untouched. `shade/playground/vgeo_runtime/` is that, on screen.
741
- **Still not done**: flip validation (§2, extension 3).
742
- - **M1 — GPU cut** — **done**: selection buffer + compute cut pass; wants/touches readback wired.
743
- `runtime/` holds all of it and [runtime/RUNTIME_PLAN.md](./runtime/RUNTIME_PLAN.md) is its design.
744
- Acceptance was parity with M0 and selection cost measured, and both were taken:
745
-
746
- *Parity* is `runtime/cut/vgeo_cut_agreement.spec.js` — the kernels run under the WGSL emulator
747
- against a real container and their drawn cluster **multiset** is held to exactly what
748
- `vgeo_select_cut` selects, over seven cameras and four thresholds, fully resident and with pages
749
- withheld, one instance and two. Multiset rather than set because a duplicate is the failure that
750
- reads as corrupt geometry rather than as a missing page.
751
-
752
- *Measured*, on an nvidia/lovelace adapter, a 13-page torus opened holding only its root page and
753
- streamed entirely by the cut's own wants: 50 drawn clusters at τ=0.5 down to 8 at τ=16, monotone,
754
- visiting 9 pages down to 5, clamping to zero as pages arrive, dropping nothing. Half the frames
755
- skip feedback entirely under a two-deep readback cap, which is the virtual-texture contract and
756
- costs streaming latency rather than pixels.
757
-
758
- The flat-vs-hierarchy decision data is *not* what M1 produced, and §7 already says why: §4 pinned
759
- the traversal and the question stopped being a measurement to take. What the cut measures instead
760
- is that the hierarchy's own accelerator is sound — the node metric's `max_parent_error` cull fires
761
- across the whole agreement sweep and changes not one selected cluster.
762
- - **M2 — format + streaming** — *the RAM tier landed early*: `.vgeo` container, pager,
763
- residency tiers with the §6 invariants (page-slot pool, address-migration propagation,
764
- occlusion-aware wants, touch-LRU, in-flight pinning), budgets, eviction. Position
765
- quantization lands here (encode + struct + decode shaders). Acceptance: cold-load TTFP =
766
- header + root page regardless of asset size; streaming never produces holes; page
767
- size/codec chosen on measurement.
768
- **Done ahead of M1**: the container and its validator; the RAM-tier pager with residency,
769
- activation and eviction (`format/read/`); TTFP, which is header + level table + root page and
770
- measured to hold on a 27M-triangle asset; the page codec, chosen on measurement (§5).
771
- **Done with M1**: the VRAM page pool (`runtime/GPUVirtualGeometryPool`) with a byte budget and
772
- touch-LRU under a protect window, and the want record now naming the page it was discovered
773
- *through* — which is what in-flight pinning was blocked on, since the format stores parent counts
774
- rather than parent references and nothing below the cut could name that page.
775
- **Still M2's**: address-migration propagation, occlusion-aware wants, the in-flight pin itself,
776
- and position quantization.
777
- - **M3 — hardening**: prefetch, disk page store decision, budget defaults, low-limit
778
- adapters (τ fallback), stress on CAD-class input; browser builder-harness proof
779
- (build a mid-size asset in-tab).
780
-
781
- ## 11. References
782
-
783
- - Karis, *Nanite — A Deep Dive*, SIGGRAPH 2021 (Advances in Real-Time Rendering).
784
- - Michaelides & Vaisse, *Maximizing Graphics Performance with Flexible Virtualized
785
- Geometry*, GDC 2024 (Virtuos "Hi-Res Vision").
786
- - Refinement-metric musings: https://share.gemini.google/AfYiNvAB2kt0
787
- - `../meshlet/README.md` — RE Engine meshlet compression notes, nv_cluster_builder.