@woosh/meep-engine 3.28.0 → 3.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (685) hide show
  1. package/build/bundle-worker-image-decoder.js +1 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/package.json +1 -1
  4. package/src/core/binary/BinaryBuffer.d.ts.map +1 -1
  5. package/src/core/binary/BinaryBuffer.js +14 -2
  6. package/src/core/binary/BitSet.d.ts +16 -0
  7. package/src/core/binary/BitSet.d.ts.map +1 -1
  8. package/src/core/binary/BitSet.js +159 -49
  9. package/src/core/binary/allocator/OffsetAllocator.d.ts +48 -0
  10. package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
  11. package/src/core/binary/allocator/OffsetAllocator.js +208 -9
  12. package/src/core/binary/compression/compress_bytes.d.ts +20 -0
  13. package/src/core/binary/compression/compress_bytes.d.ts.map +1 -0
  14. package/src/core/binary/compression/compress_bytes.js +35 -0
  15. package/src/core/binary/compression/decompress_bytes_into.d.ts +30 -0
  16. package/src/core/binary/compression/decompress_bytes_into.d.ts.map +1 -0
  17. package/src/core/binary/compression/decompress_bytes_into.js +87 -0
  18. package/src/core/binary/compression/deflate_decompress_bound.d.ts +27 -0
  19. package/src/core/binary/compression/deflate_decompress_bound.d.ts.map +1 -0
  20. package/src/core/binary/compression/deflate_decompress_bound.js +32 -0
  21. package/src/core/bvh2/bvh3/BVH.d.ts.map +1 -1
  22. package/src/core/bvh2/bvh3/BVH.js +14 -1
  23. package/src/core/cache/FrequencySketch.d.ts +3 -1
  24. package/src/core/cache/FrequencySketch.d.ts.map +1 -1
  25. package/src/core/cache/FrequencySketch.js +10 -4
  26. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
  27. package/src/core/cache/wtinylfu/CacheWTinylfu.js +24 -2
  28. package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts +24 -0
  29. package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts.map +1 -0
  30. package/src/core/geom/3d/quadric/quadric_attribute_add.js +31 -0
  31. package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts +46 -0
  32. package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts.map +1 -0
  33. package/src/core/geom/3d/quadric/quadric_attribute_reduce.js +104 -0
  34. package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts +62 -0
  35. package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts.map +1 -0
  36. package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.js +158 -0
  37. package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts +21 -0
  38. package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts.map +1 -0
  39. package/src/core/geom/3d/quadric/quadric_attribute_size.js +24 -0
  40. package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts +34 -0
  41. package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts.map +1 -0
  42. package/src/core/geom/3d/quadric/quadric_attribute_solve.js +55 -0
  43. package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts +28 -0
  44. package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts.map +1 -0
  45. package/src/core/geom/3d/quadric/quadric_attribute_sub.js +35 -0
  46. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts +15 -0
  47. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts.map +1 -1
  48. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +35 -0
  49. package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts +93 -0
  50. package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts.map +1 -0
  51. package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.js +128 -0
  52. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts +20 -0
  53. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.d.ts.map +1 -1
  54. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_vertex_quadrics.js +69 -4
  55. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts +38 -0
  56. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts.map +1 -0
  57. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.js +113 -0
  58. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +93 -17
  59. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  60. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +930 -69
  61. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts +22 -0
  62. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts.map +1 -0
  63. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.js +46 -0
  64. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts +19 -0
  65. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts.map +1 -0
  66. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.js +49 -0
  67. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts +25 -0
  68. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.d.ts.map +1 -0
  69. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_fold_limit.js +228 -0
  70. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts +13 -0
  71. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.d.ts.map +1 -1
  72. package/src/core/geom/3d/topology/struct/binary/query/bt_edge_collapse_would_fold.js +10 -128
  73. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.d.ts.map +1 -1
  74. package/src/core/geom/3d/topology/struct/binary/query/bt_mesh_build_face_bvh.js +11 -1
  75. package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts +63 -0
  76. package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts.map +1 -0
  77. package/src/core/graph/layout/layered/BoxLayoutOptions.js +80 -0
  78. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.d.ts +35 -0
  79. package/src/core/graph/layout/layered/LayoutBoxGraph.d.ts.map +1 -0
  80. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.js +53 -1
  81. package/src/core/graph/layout/layered/layout_assign_coordinates.d.ts.map +1 -0
  82. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.js +72 -68
  83. package/src/core/graph/layout/layered/layout_assign_layers.d.ts.map +1 -0
  84. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.js +4 -2
  85. package/src/core/graph/layout/layered/layout_break_cycles.d.ts.map +1 -0
  86. package/src/core/graph/layout/layered/layout_insert_lanes.d.ts.map +1 -0
  87. package/src/core/graph/layout/layered/layout_order_layers.d.ts.map +1 -0
  88. package/src/core/graph/layout/layered/layout_orient_links.d.ts.map +1 -0
  89. package/src/core/graph/mn_graph_collapse_weighted_edge.js +6 -6
  90. package/src/core/math/lookup/ParameterLookupTable.d.ts.map +1 -1
  91. package/src/core/math/lookup/ParameterLookupTable.js +10 -5
  92. package/src/core/math/noise/sdnoise.d.ts.map +1 -1
  93. package/src/core/math/noise/sdnoise.js +16 -10
  94. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +7 -51
  95. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
  96. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +8 -69
  97. package/src/core/model/node-graph/visual/layout/layout_build_problem.js +1 -1
  98. package/src/core/model/node-graph/visual/layout/layout_node_graph.js +6 -6
  99. package/src/core/model/node-graph/visual/layout/route_grid_build.js +1 -1
  100. package/src/core/model/object/ImmutableObjectPool.d.ts.map +1 -1
  101. package/src/core/model/object/ImmutableObjectPool.js +17 -7
  102. package/src/core/process/executor/ConcurrentExecutor.d.ts +1 -5
  103. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  104. package/src/core/process/executor/ConcurrentExecutor.js +95 -1
  105. package/src/core/process/task/task_find_dependency_cycle.d.ts +17 -0
  106. package/src/core/process/task/task_find_dependency_cycle.d.ts.map +1 -0
  107. package/src/core/process/task/task_find_dependency_cycle.js +81 -0
  108. package/src/core/wfc/WFCCellChoice.d.ts +17 -0
  109. package/src/core/wfc/WFCCellChoice.d.ts.map +1 -0
  110. package/src/core/wfc/WFCCellChoice.js +36 -0
  111. package/src/core/wfc/WFCLattice.d.ts +105 -0
  112. package/src/core/wfc/WFCLattice.d.ts.map +1 -0
  113. package/src/core/wfc/WFCLattice.js +235 -0
  114. package/src/core/wfc/WFCRuleTable.d.ts +172 -0
  115. package/src/core/wfc/WFCRuleTable.d.ts.map +1 -0
  116. package/src/core/wfc/WFCRuleTable.js +334 -0
  117. package/src/core/wfc/WFCState.d.ts +273 -0
  118. package/src/core/wfc/WFCState.d.ts.map +1 -0
  119. package/src/core/wfc/WFCState.js +552 -0
  120. package/src/core/wfc/WFCStatus.d.ts +11 -0
  121. package/src/core/wfc/WFCStatus.d.ts.map +1 -0
  122. package/src/core/wfc/WFCStatus.js +28 -0
  123. package/src/core/wfc/WFCTileWeights.d.ts +48 -0
  124. package/src/core/wfc/WFCTileWeights.d.ts.map +1 -0
  125. package/src/core/wfc/WFCTileWeights.js +86 -0
  126. package/src/core/wfc/WFC_UNRESOLVED.d.ts +12 -0
  127. package/src/core/wfc/WFC_UNRESOLVED.d.ts.map +1 -0
  128. package/src/core/wfc/WFC_UNRESOLVED.js +11 -0
  129. package/src/core/wfc/wfc_estimate_bytes.d.ts +24 -0
  130. package/src/core/wfc/wfc_estimate_bytes.d.ts.map +1 -0
  131. package/src/core/wfc/wfc_estimate_bytes.js +47 -0
  132. package/src/core/wfc/wfc_rule_table_tile_components.d.ts +27 -0
  133. package/src/core/wfc/wfc_rule_table_tile_components.d.ts.map +1 -0
  134. package/src/core/wfc/wfc_rule_table_tile_components.js +80 -0
  135. package/src/core/wfc/wfc_solve.d.ts +21 -0
  136. package/src/core/wfc/wfc_solve.d.ts.map +1 -0
  137. package/src/core/wfc/wfc_solve.js +154 -0
  138. package/src/core/wfc/wfc_state_apply_boundary.d.ts +18 -0
  139. package/src/core/wfc/wfc_state_apply_boundary.d.ts.map +1 -0
  140. package/src/core/wfc/wfc_state_apply_boundary.js +61 -0
  141. package/src/core/wfc/wfc_state_assign.d.ts +11 -0
  142. package/src/core/wfc/wfc_state_assign.d.ts.map +1 -0
  143. package/src/core/wfc/wfc_state_assign.js +36 -0
  144. package/src/core/wfc/wfc_state_ban_bits.d.ts +17 -0
  145. package/src/core/wfc/wfc_state_ban_bits.d.ts.map +1 -0
  146. package/src/core/wfc/wfc_state_ban_bits.js +81 -0
  147. package/src/core/wfc/wfc_state_collapse.d.ts +12 -0
  148. package/src/core/wfc/wfc_state_collapse.d.ts.map +1 -0
  149. package/src/core/wfc/wfc_state_collapse.js +73 -0
  150. package/src/core/wfc/wfc_state_flush_dirty.d.ts +12 -0
  151. package/src/core/wfc/wfc_state_flush_dirty.d.ts.map +1 -0
  152. package/src/core/wfc/wfc_state_flush_dirty.js +39 -0
  153. package/src/core/wfc/wfc_state_propagate.d.ts +14 -0
  154. package/src/core/wfc/wfc_state_propagate.d.ts.map +1 -0
  155. package/src/core/wfc/wfc_state_propagate.js +155 -0
  156. package/src/core/wfc/wfc_state_read.d.ts +11 -0
  157. package/src/core/wfc/wfc_state_read.d.ts.map +1 -0
  158. package/src/core/wfc/wfc_state_read.js +38 -0
  159. package/src/core/wfc/wfc_state_restrict.d.ts +18 -0
  160. package/src/core/wfc/wfc_state_restrict.d.ts.map +1 -0
  161. package/src/core/wfc/wfc_state_restrict.js +39 -0
  162. package/src/core/wfc/wfc_state_set_eligible.d.ts +24 -0
  163. package/src/core/wfc/wfc_state_set_eligible.d.ts.map +1 -0
  164. package/src/core/wfc/wfc_state_set_eligible.js +49 -0
  165. package/src/core/wfc/wfc_state_undo_to.d.ts +13 -0
  166. package/src/core/wfc/wfc_state_undo_to.d.ts.map +1 -0
  167. package/src/core/wfc/wfc_state_undo_to.js +84 -0
  168. package/src/core/wfc/wfc_words_per_cell.d.ts +11 -0
  169. package/src/core/wfc/wfc_words_per_cell.d.ts.map +1 -0
  170. package/src/core/wfc/wfc_words_per_cell.js +16 -0
  171. package/src/engine/ecs/EntityComponentDataset.d.ts +8 -0
  172. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  173. package/src/engine/ecs/EntityComponentDataset.js +37 -14
  174. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts +8 -1
  175. package/src/engine/ecs/fow/FogOfWarRevealerSystem.d.ts.map +1 -1
  176. package/src/engine/ecs/fow/FogOfWarRevealerSystem.js +37 -2
  177. package/src/engine/ecs/guid/UUID.d.ts.map +1 -1
  178. package/src/engine/ecs/guid/UUID.js +45 -17
  179. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.d.ts.map +1 -1
  180. package/src/engine/ecs/ik/OneBoneSurfaceAlignmentSolver.js +3 -1
  181. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.d.ts.map +1 -1
  182. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSystem.js +366 -360
  183. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.d.ts.map +1 -1
  184. package/src/engine/graphics/texture/sampler/resize/sampler2d_scale_down_linear.js +24 -10
  185. package/src/engine/graphics3/DebugDrawSystem.d.ts +7 -5
  186. package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
  187. package/src/engine/graphics3/DebugDrawSystem.js +206 -203
  188. package/src/engine/graphics3/DecalSystem.d.ts +4 -5
  189. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  190. package/src/engine/graphics3/DecalSystem.js +5 -5
  191. package/src/engine/graphics3/FogOfWarSystem.d.ts +7 -12
  192. package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -1
  193. package/src/engine/graphics3/FogOfWarSystem.js +8 -12
  194. package/src/engine/graphics3/prefab/prefab_instantiate.js +230 -230
  195. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.d.ts.map +1 -1
  196. package/src/engine/grid/grid2transform/GridPosition2TransformSystem.js +24 -5
  197. package/src/engine/interpolation/Interpoland.d.ts +7 -1
  198. package/src/engine/interpolation/Interpoland.d.ts.map +1 -1
  199. package/src/engine/interpolation/Interpoland.js +8 -1
  200. package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
  201. package/src/engine/interpolation/InterpolationSystem.js +3 -0
  202. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts +5 -3
  203. package/src/engine/interpolation/TransformPoseSerializationAdapter.d.ts.map +1 -1
  204. package/src/engine/interpolation/TransformPoseSerializationAdapter.js +14 -3
  205. package/src/engine/interpolation/pose_interpoland.d.ts.map +1 -1
  206. package/src/engine/interpolation/pose_interpoland.js +2 -0
  207. package/src/engine/network/orchestrator/NetworkPeer.d.ts.map +1 -1
  208. package/src/engine/network/orchestrator/NetworkPeer.js +15 -10
  209. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -1
  210. package/src/engine/physics/cloth/ecs/ClothWorld.js +17 -0
  211. package/src/engine/physics/mls-mpm/MLS_MPM.d.ts.map +1 -1
  212. package/src/engine/physics/mls-mpm/MLS_MPM.js +14 -0
  213. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +2 -1
  214. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
  215. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +1 -1
  216. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -0
  217. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts.map +1 -1
  218. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -0
  219. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts.map +1 -1
  220. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +2 -1
  221. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
  222. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +12 -3
  223. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -0
  224. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts.map +1 -1
  225. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -0
  226. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts.map +1 -1
  227. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
  228. package/src/engine/sound/sopra/runtime/EventInstance.js +4 -1
  229. package/src/format/image/avif/av1/decode/decode_coefficients.d.ts.map +1 -1
  230. package/src/format/image/avif/av1/decode/decode_coefficients.js +248 -259
  231. package/src/format/image/avif/av1/decode/read_golomb.d.ts +21 -0
  232. package/src/format/image/avif/av1/decode/read_golomb.d.ts.map +1 -0
  233. package/src/format/image/avif/av1/decode/read_golomb.js +49 -0
  234. package/src/format/image/avif/av1/filter/loop_restoration.js +461 -451
  235. package/src/format/image/avif/av1/util/round2.d.ts +19 -2
  236. package/src/format/image/avif/av1/util/round2.d.ts.map +1 -1
  237. package/src/format/image/avif/av1/util/round2.js +51 -27
  238. package/src/format/image/avif/heif/parse_avif_file.js +24 -0
  239. package/src/shade/RENDERER_CONTRACT.md +12 -7
  240. package/src/shade/playground/caldera/README.md +529 -0
  241. package/src/shade/playground/caldera/caldera_format.d.ts +151 -0
  242. package/src/shade/playground/caldera/caldera_format.d.ts.map +1 -0
  243. package/src/shade/playground/caldera/caldera_format.js +241 -0
  244. package/src/shade/playground/caldera/index.html +126 -0
  245. package/src/shade/playground/caldera/main.d.ts.map +1 -0
  246. package/src/shade/playground/caldera/main.js +1565 -0
  247. package/src/shade/playground/mesh_row_ceiling/README.md +85 -0
  248. package/src/shade/playground/mesh_row_ceiling/index.html +31 -0
  249. package/src/shade/playground/mesh_row_ceiling/main.d.ts +2 -0
  250. package/src/shade/playground/mesh_row_ceiling/main.d.ts.map +1 -0
  251. package/src/shade/playground/mesh_row_ceiling/main.js +389 -0
  252. package/src/shade/playground/vgeo_runtime/README.md +5 -3
  253. package/src/shade/playground/vgeo_runtime/main.js +3 -2
  254. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +3 -3
  255. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -1
  256. package/src/shade/playground/vgeo_runtime/sample_asset.js +3 -3
  257. package/src/shade/playground/vgeo_scene/README.md +103 -24
  258. package/src/shade/playground/vgeo_scene/main.js +525 -24
  259. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  260. package/src/shade/renderer/GraphicsContext.js +6 -0
  261. package/src/shade/renderer/Renderer.d.ts +2 -0
  262. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  263. package/src/shade/renderer/Renderer.js +23 -0
  264. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  265. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +100 -8
  266. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  267. package/src/shade/renderer/buffer/table/GPUTypedTable.js +14 -0
  268. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +69 -33
  269. package/src/shade/renderer/extension/RenderExtension.d.ts +15 -18
  270. package/src/shade/renderer/extension/RenderExtension.d.ts.map +1 -1
  271. package/src/shade/renderer/extension/RenderExtension.js +15 -19
  272. package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts +5 -5
  273. package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts.map +1 -1
  274. package/src/shade/renderer/extension/RenderExtensionRegistry.js +29 -166
  275. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  276. package/src/shade/renderer/geometry/GPUGeometryManager.js +73 -16
  277. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  278. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +18 -3
  279. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +663 -0
  280. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +100 -5
  281. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  282. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +688 -81
  283. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +540 -0
  284. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +155 -64
  285. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +76 -16
  286. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +3 -3
  287. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +448 -0
  288. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +44 -7
  289. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  290. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +48 -9
  291. package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts +40 -0
  292. package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts.map +1 -0
  293. package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.js +24 -0
  294. package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts +91 -0
  295. package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts.map +1 -0
  296. package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.js +91 -0
  297. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts +24 -0
  298. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts.map +1 -0
  299. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.js +25 -0
  300. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts +77 -0
  301. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts.map +1 -0
  302. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.js +84 -0
  303. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +19 -6
  304. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -1
  305. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.js +20 -7
  306. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts +19 -0
  307. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts.map +1 -0
  308. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.js +58 -0
  309. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts +18 -0
  310. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts.map +1 -0
  311. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.js +116 -0
  312. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts +18 -0
  313. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts.map +1 -0
  314. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +545 -0
  315. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts +16 -0
  316. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts.map +1 -0
  317. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.js +110 -0
  318. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +2 -27
  319. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -1
  320. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.js +120 -1564
  321. package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts +26 -0
  322. package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts.map +1 -0
  323. package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.js +123 -0
  324. package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts +26 -0
  325. package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts.map +1 -0
  326. package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.js +78 -0
  327. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts +24 -0
  328. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts.map +1 -0
  329. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.js +337 -0
  330. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts +29 -0
  331. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts.map +1 -0
  332. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.js +147 -0
  333. package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts +37 -0
  334. package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts.map +1 -0
  335. package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.js +243 -0
  336. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts +29 -0
  337. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts.map +1 -0
  338. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.js +117 -0
  339. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts +46 -0
  340. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts.map +1 -0
  341. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +74 -0
  342. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -0
  343. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +78 -0
  344. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +12 -7
  345. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -1
  346. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.js +33 -11
  347. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts +58 -0
  348. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts.map +1 -0
  349. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.js +79 -0
  350. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts +39 -0
  351. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts.map +1 -0
  352. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.js +68 -0
  353. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts +53 -0
  354. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts.map +1 -0
  355. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.js +243 -0
  356. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +32 -7
  357. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -1
  358. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +243 -24
  359. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +12 -8
  360. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -1
  361. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.js +58 -50
  362. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +5 -0
  363. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -1
  364. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.js +13 -1
  365. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts +38 -0
  366. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts.map +1 -0
  367. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.js +81 -0
  368. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +23 -5
  369. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -1
  370. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.js +28 -21
  371. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.js +1 -1
  372. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -1
  373. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.js +224 -60
  374. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +15 -0
  375. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -1
  376. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.js +17 -0
  377. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +17 -0
  378. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -1
  379. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.js +19 -0
  380. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +10 -1
  381. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -1
  382. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.js +103 -9
  383. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +14 -2
  384. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -1
  385. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.js +38 -14
  386. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts +6 -2
  387. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  388. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +27 -4
  389. package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts +32 -0
  390. package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts.map +1 -0
  391. package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.js +93 -0
  392. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +20 -6
  393. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  394. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +22 -8
  395. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts +16 -0
  396. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts.map +1 -0
  397. package/src/shade/renderer/geometry/virtual/format/attribute/{vgeo_layers_copy_vertex.js → vgeo_layers_copy_corner.js} +8 -8
  398. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts +18 -7
  399. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts.map +1 -1
  400. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +19 -8
  401. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.d.ts +3 -3
  402. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +3 -3
  403. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts +27 -0
  404. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts.map +1 -0
  405. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.js +97 -0
  406. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts +7 -3
  407. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts.map +1 -1
  408. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +21 -11
  409. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts +21 -0
  410. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts.map +1 -0
  411. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.js +41 -0
  412. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +19 -1
  413. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
  414. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +20 -1
  415. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts +25 -0
  416. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts.map +1 -0
  417. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.js +24 -0
  418. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts +5 -0
  419. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts.map +1 -1
  420. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +5 -0
  421. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.d.ts.map +1 -1
  422. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +5 -0
  423. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts +10 -3
  424. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts.map +1 -1
  425. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +37 -3
  426. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +21 -13
  427. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
  428. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +37 -13
  429. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts +8 -2
  430. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts.map +1 -1
  431. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +14 -2
  432. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +18 -0
  433. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
  434. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +19 -1
  435. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +6 -0
  436. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts +21 -0
  437. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts.map +1 -0
  438. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.js +20 -0
  439. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts +9 -0
  440. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts.map +1 -1
  441. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +10 -1
  442. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +1 -2
  443. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +10 -3
  444. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts +20 -0
  445. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts.map +1 -0
  446. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.js +43 -0
  447. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +4 -2
  448. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
  449. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +8 -4
  450. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  451. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +2 -2
  452. package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts +6 -4
  453. package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts.map +1 -1
  454. package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.js +6 -4
  455. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +13 -2
  456. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  457. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +13 -2
  458. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +5 -1
  459. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -1
  460. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +14 -1
  461. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -1
  462. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +1 -0
  463. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts +4 -1
  464. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts.map +1 -1
  465. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.js +53 -11
  466. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
  467. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +7 -3
  468. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts +12 -2
  469. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  470. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +111 -33
  471. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts +6 -2
  472. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  473. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +112 -42
  474. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts +2 -2
  475. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  476. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +51 -21
  477. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +385 -0
  478. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
  479. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +9 -0
  480. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
  481. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +14 -1
  482. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  483. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +34 -0
  484. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -1
  485. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +18 -0
  486. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  487. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +47 -19
  488. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +1 -0
  489. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +22 -4
  490. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +7 -2
  491. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -1
  492. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +8 -3
  493. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
  494. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +10 -0
  495. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
  496. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +13 -11
  497. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -1
  498. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +13 -11
  499. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
  500. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +4 -0
  501. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
  502. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +12 -3
  503. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  504. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +3 -4
  505. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts +6 -2
  506. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
  507. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +90 -12
  508. package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts +22 -0
  509. package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts.map +1 -0
  510. package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.js +21 -0
  511. package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.d.ts.map +1 -1
  512. package/src/shade/renderer/light/environment/rgbe/RGBE_ReadPixels_RLE.js +7 -1
  513. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  514. package/src/shade/renderer/lightmap/GPULightMap.js +349 -336
  515. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  516. package/src/shade/renderer/loader/gltf/load_gltf.js +1137 -1128
  517. package/src/shade/renderer/particles/DESIGN.md +14 -5
  518. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +25 -10
  519. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  520. package/src/shade/renderer/particles/GPUParticleSystem.js +182 -38
  521. package/src/shade/renderer/particles/graph/groups/simplex_reference.d.ts.map +1 -1
  522. package/src/shade/renderer/particles/graph/groups/simplex_reference.js +16 -10
  523. package/src/shade/renderer/particles/graph_particles.d.ts +4 -2
  524. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  525. package/src/shade/renderer/particles/graph_particles.js +8 -2
  526. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.d.ts.map +1 -1
  527. package/src/shade/renderer/particles/optimizer/particle_vm_semantics.js +8 -2
  528. package/src/shade/renderer/particles/optimizer/pass_fold_constants.d.ts.map +1 -1
  529. package/src/shade/renderer/particles/optimizer/pass_fold_constants.js +10 -4
  530. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  531. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +13 -8
  532. package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts +31 -0
  533. package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.d.ts.map +1 -0
  534. package/src/shade/renderer/particles/shaders/shader_particle_init_free_list.js +62 -0
  535. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  536. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +3 -6
  537. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts +13 -0
  538. package/src/shade/renderer/particles/vm/ParticleVMReference.d.ts.map +1 -1
  539. package/src/shade/renderer/particles/vm/ParticleVMReference.js +95 -17
  540. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  541. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +4 -12
  542. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +21 -0
  543. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -0
  544. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +28 -0
  545. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +0 -2
  546. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  547. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +4 -5
  548. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  549. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +5 -3
  550. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.d.ts.map +1 -1
  551. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.js +11 -4
  552. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  553. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +4 -3
  554. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
  555. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +3 -0
  556. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
  557. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +94 -90
  558. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  559. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +13 -5
  560. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
  561. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +96 -89
  562. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
  563. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +3 -1
  564. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.d.ts.map +1 -1
  565. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.js +4 -1
  566. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
  567. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +100 -89
  568. package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.d.ts.map +1 -1
  569. package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.js +31 -7
  570. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
  571. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +1 -7
  572. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
  573. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +161 -153
  574. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  575. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +2 -7
  576. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  577. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +19 -2
  578. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  579. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +4 -8
  580. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  581. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +5 -8
  582. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  583. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +19 -2
  584. package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
  585. package/src/shade/renderer/shader/type/WebGPUStruct.js +12 -0
  586. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +25 -0
  587. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts.map +1 -0
  588. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +24 -0
  589. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +10 -0
  590. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  591. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +30 -21
  592. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts +8 -2
  593. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
  594. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +41 -6
  595. package/src/shade/wgsl/emulator/WGSLJavaScriptCompiler.js +2383 -2356
  596. package/src/view/tooltip/DomTooltipManager.d.ts +7 -0
  597. package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
  598. package/src/view/tooltip/DomTooltipManager.js +22 -0
  599. package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
  600. package/src/view/tooltip/DomTooltipObserver.js +13 -3
  601. package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts.map +0 -1
  602. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts.map +0 -1
  603. package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts.map +0 -1
  604. package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts.map +0 -1
  605. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +0 -1
  606. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +0 -1
  607. package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts.map +0 -1
  608. package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts +0 -61
  609. package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts.map +0 -1
  610. package/src/shade/playground/vgeo_viewer/main.d.ts.map +0 -1
  611. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts +0 -18
  612. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +0 -1
  613. package/src/shade/playground/vgeo_viewer/select_cut.d.ts +0 -173
  614. package/src/shade/playground/vgeo_viewer/select_cut.d.ts.map +0 -1
  615. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts +0 -52
  616. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts.map +0 -1
  617. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts +0 -24
  618. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts.map +0 -1
  619. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts +0 -32
  620. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts.map +0 -1
  621. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts +0 -59
  622. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts.map +0 -1
  623. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts +0 -44
  624. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts.map +0 -1
  625. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts +0 -20
  626. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts.map +0 -1
  627. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts +0 -73
  628. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts.map +0 -1
  629. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +0 -13
  630. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +0 -1
  631. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts +0 -12
  632. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts.map +0 -1
  633. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts +0 -36
  634. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts.map +0 -1
  635. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts +0 -13
  636. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts.map +0 -1
  637. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts +0 -19
  638. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts.map +0 -1
  639. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts +0 -29
  640. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts.map +0 -1
  641. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts +0 -31
  642. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts.map +0 -1
  643. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts +0 -18
  644. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts.map +0 -1
  645. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts +0 -14
  646. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts.map +0 -1
  647. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts +0 -12
  648. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts.map +0 -1
  649. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts +0 -14
  650. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts.map +0 -1
  651. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts +0 -11
  652. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts.map +0 -1
  653. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts +0 -46
  654. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts.map +0 -1
  655. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +0 -22
  656. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +0 -1
  657. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts +0 -29
  658. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts.map +0 -1
  659. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts +0 -9
  660. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts.map +0 -1
  661. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts +0 -10
  662. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts.map +0 -1
  663. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts +0 -15
  664. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts.map +0 -1
  665. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts +0 -16
  666. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts.map +0 -1
  667. package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts +0 -10
  668. package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts.map +0 -1
  669. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts +0 -17
  670. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts.map +0 -1
  671. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts +0 -3
  672. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts.map +0 -1
  673. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts +0 -8
  674. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts.map +0 -1
  675. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.d.ts +0 -0
  676. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.d.ts +0 -0
  677. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.d.ts +0 -0
  678. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.js +0 -0
  679. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.d.ts +0 -0
  680. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.js +0 -0
  681. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.d.ts +0 -0
  682. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.js +0 -0
  683. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.d.ts +0 -0
  684. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.js +0 -0
  685. /package/src/shade/playground/{vgeo_viewer → caldera}/main.d.ts +0 -0
@@ -0,0 +1,448 @@
1
+ # Wedges — plan, 2026-09-18 — **HALF OF IT REVERTED 2026-09-19**
2
+
3
+ > **The partition stayed. The crease angle went.** This document was written as one idea and it is
4
+ > two, and only one of them survived being measured in pixels.
5
+ >
6
+ > **What went: inferring a hard edge from an angle.** `VGEO_CREASE_ANGLE`, 45 degrees, applied
7
+ > where the builder has to *synthesize* a normal because the source authored none. It made a
8
+ > POSITION-only cube ship three normals at each corner instead of one that is 54.7 degrees wrong for
9
+ > every face using it, which is a real improvement to look at. Measured against a true LOD 0
10
+ > reference it bought nothing on any object tested, and on high-frequency relief it *cost*: the
11
+ > attribute quadric's least-squares fit lands near the neighbourhood's mean normal, which is the
12
+ > right filtered answer for a Lambertian surface, and bucketing each tile face on its own returns
13
+ > that one face's normal exactly instead. It was also most of the price — on the pebble-scatter
14
+ > geometry the wedge build cost +3.37% of asset bytes over pre-attribute with the threshold in and
15
+ > +0.97% without it.
16
+ >
17
+ > **What stayed: carrying the shading frame a source actually authored.** `BtWedgeSet`,
18
+ > `vgeo_mesh_wedges`, and the simplifier's wedge bookkeeping. The partition is decided by
19
+ > `loop_data.record_equal()` — byte equality of what the source gave — so it needs no threshold and
20
+ > makes no judgement about content. On a UE5 courtyard gate arch (188,180 triangles, authored
21
+ > normals and UVs), taking it out costs, at **equal drawn cluster count**, mean luminance against
22
+ > that build's own LOD 0:
23
+ >
24
+ > | ue_arch | τ=0.5 | τ=1 | τ=2 | τ=4 |
25
+ > |---|---|---|---|---|
26
+ > | without the partition | +1.18 | +6.50 | +6.91 | +8.82 |
27
+ > | *same build, second page load* | −0.06 | +0.07 | +0.06 | +0.06 |
28
+ >
29
+ > That is 20–27% more error against a noise floor of 0.07, and it reproduces. At τ=0.5, 1 and 2 the
30
+ > build without the partition is not even trading: it draws **more** clusters *and* errs more. Only
31
+ > τ=4 is a real trade (−20% clusters for +27% error), and interpolated it sits on the same curve.
32
+ >
33
+ > **It is a normals result, not a UV one.** The playground binds one untextured grey
34
+ > `StandardShadeMaterial` and samples no maps, so nothing in that table can come from texture
35
+ > coordinates. Seams are carried by the same mechanism and that is worth having, but it is a
36
+ > different question and it is not what was measured here.
37
+ >
38
+ > **And it helps where the frame is synthesized too**, now that the threshold is gone. Corners still
39
+ > group, by connectivity rather than by angle: at a mesh boundary or a non-manifold fin there is no
40
+ > second face to average with, so each side keeps its own normal instead of inventing a join the
41
+ > mesh does not make. On the pebble scatter that costs +0.45% of bytes over having no partition at
42
+ > all, and buys — again at equal drawn clusters, against pre-attribute —
43
+ >
44
+ > | mesh 1400 | τ=0.5 | τ=1 | τ=2 | τ=4 |
45
+ > |---|---|---|---|---|
46
+ > | no partition | +1.55 | +4.87 | +5.42 | +7.08 |
47
+ > | partition, no threshold | +4.95 | **+4.03** | **−2.51** | **−5.30** |
48
+ >
49
+ > Pareto-dominant at τ=1, 2 and 4 — fewer clusters *and* less error — and at the two coarse cuts it
50
+ > beats pre-attribute outright. The finest cut is worse, and that is the honest caveat.
51
+ >
52
+ > **The split is proved by byte identity, not only by pixels.** Built with the threshold removed,
53
+ > the gate arch is byte-identical to the full wedge build (the whole win preserved, nothing else
54
+ > changed), and a UE5 throne-room floor is byte-identical to a build with no wedge code at all.
55
+ >
56
+ > **What this does *not* claim.** Not that the N-dimensional attribute quadrics are themselves a
57
+ > pixel win — measured the same way they are a wash-to-worse on three of five objects, and that
58
+ > deserves its own investigation. Not that Zorah's normals are fine: on a POSITION-only source every
59
+ > normal is synthesized by `vgeo_normalize_source`, area-weighted, after the weld, nothing pinned,
60
+ > so a hard edge cannot exist at *any* level including the finest — that is the flat-looking tiled
61
+ > surface, it is upstream of everything here, and the UE5 rebuild is what fixes it. And not that the
62
+ > cut is free: on that floor, pre-attribute draws 80 clusters where both attribute builds draw 513
63
+ > at the same τ, a 6x difference nobody asked for.
64
+ >
65
+ > **The pixel harness is the other thing worth keeping** (`container_diff.mjs`, scratchpad pattern).
66
+ > Four ways it lied before it worked, each of which had already produced a confident wrong answer:
67
+ >
68
+ > 1. **TAA.** Two captures of one static camera differ by mean 4.0 with *not one pixel identical*.
69
+ > Pinning the jitter is necessary and insufficient — the resolve un-jitters against its own copy,
70
+ > so one run settles to 0.004 and an identical one plateaus at 0.153 forever. Average 16 frames.
71
+ > 2. **Dilution.** The object covered 1–2% of the viewport, so every mean read 0.2–0.7 with p95 of
72
+ > exactly 0.0. Mask to lit pixels and print the coverage.
73
+ > 3. **The stats panel**, which prints the file name and byte counts and so differs per build by
74
+ > construction — and the crop keeping it out was slicing through the object.
75
+ > 4. **Cross-run image diffs are worthless.** The same build in two page loads, with identical
76
+ > `drawn` counts, differs by mean 15–17. Only a build's *own* LOD-0 curve is comparable.
77
+ >
78
+ > A whole scene has no reachable LOD 0 either: below about τ=0.25 the residency budget picks the cut
79
+ > and at τ→0 the renderer stops producing frames, so single containers are the only honest reference.
80
+ >
81
+ > **Compare at equal drawn clusters, never at equal τ**, which is a request and not a price — the
82
+ > wedge build drew 1,085 where pre-attribute drew 772 at the same number on the slider.
83
+ >
84
+ > **Choosing what to measure was half the work.** Three rounds of picking objects out of
85
+ > `object_bounds` gave a pebble scatter, a bare tree and a set of beams, all nearly pure silhouette,
86
+ > and the difference image is then an outline with black interiors — which says nothing about
87
+ > shading, since attribute quadrics trade geometric accuracy for attribute accuracy. Manifest rows
88
+ > are `(mesh, primitive)`; carry the primitive or you build a different object, as a row claiming
89
+ > 289k triangles did when it built 2.04M. The UE5 rebuild's meshes have *names*, which is worth more
90
+ > than any heuristic over bounds.
91
+
92
+
93
+ `ATTRIBUTE_PLAN_2026_09_18.md` §7, taken up. That plan fixed *how a coarse vertex is given its
94
+ shading values*; this one fixes the fact that a vertex can only hold **one set of them**, so every
95
+ hard edge and every texture seam in a source is destroyed before the builder has done anything at
96
+ all.
97
+
98
+ ## 1. The defect, restated with its measurement
99
+
100
+ `BinaryTopology` carries one attribute record per vertex, and `bt_merge_vertices_by_distance` says
101
+ plainly what it does with that: *"Only considers distance, no other attributes are accounted for."*
102
+ `bt_vertex_replace` re-points references and orphans the victim's record.
103
+
104
+ Measured on a hard-edged, UV-charted subdivided box pushed through `vgeo_normalize_source`: 1,734
105
+ source vertices become 1,538, the 196 splits along the twelve cube edges are gone, and **six distinct
106
+ normals survive, every one of them axis-aligned** — the master's record won outright and nothing was
107
+ blended. Every hard edge now carries a normal 90° wrong for half the triangles using it, and each UV
108
+ chart bleeds across its seam. Before a single collapse.
109
+
110
+ The weld itself is not the defect and is not going anywhere. `vgeo_normalize_source` says why: without
111
+ it *"two triangles welded along a seam still look unrelated to every query downstream, and the
112
+ clusterer shreds the mesh along every UV seam it has."* Connectivity wants the weld. Appearance does
113
+ not.
114
+
115
+ **Second consequence, and it is the visible one on Zorah.** A source that authors no normals gets them
116
+ synthesized by area-weighted averaging with no crease rule, because there is nowhere to put a second
117
+ value at a corner. At LOD 0 the angle between a vertex's normal and its own face's normal runs to a
118
+ mean of 8.6°, a p99 of 37.7° and a **max of 179.9°**. That is the soft gradient smeared across flat
119
+ panels, and no amount of work above the leaf can undo it.
120
+
121
+ ## 2. Loops are where this belongs, and the codebase already says so
122
+
123
+ `BinaryTopology` has had per-loop storage since it was written — `loop_data`, `loop_data_add_layer` —
124
+ and it has one real consumer, whose own documentation is the argument:
125
+
126
+ > UVs live on loops rather than vertices so that one geometric vertex shared by several charts carries
127
+ > a distinct UV per chart — which is exactly how a seam is represented.
128
+ > — `bt_loop_uv.js`
129
+
130
+ `bt_mesh_compact` already compacts loop data with everything else. So this is a mechanism to *use*,
131
+ not one to invent.
132
+
133
+ Two facts make it work through simplification:
134
+
135
+ - **Loop identity survives a collapse.** `bt_vertex_replace` re-points a surviving loop to the
136
+ survivor vertex and leaves the loop itself alone, so whatever a loop carries travels with it for
137
+ free. Loops of killed faces die, which is also correct — their corners no longer exist.
138
+ - **The payload encoder is already generic over it.** `vgeo_encode_cluster_payload(positions,
139
+ indices, vertex_ids, layers)` only ever does `layers[attribute].read(…, vertex_ids[v])`. Hand it
140
+ loop ids and loop layers and it is unchanged. The parameter is misnamed for that use and gets
141
+ renamed; nothing else about the encoder, the format, or the runtime moves.
142
+
143
+ ## 3. The representation
144
+
145
+ A **wedge** is a maximal set of loops at one vertex that carry the same appearance. Three pieces:
146
+
147
+ | what | where | indexed by |
148
+ |---|---|---|
149
+ | attribute values | loop data layers | loop |
150
+ | wedge id | a `u32` loop data layer | loop |
151
+ | attribute quadrics | a flat `Float64Array` | wedge |
152
+
153
+ **The invariant, and it is what the specs hold:** every loop with the same wedge id carries
154
+ byte-identical attribute values. That makes the wedge id a *cache* of an equivalence that the values
155
+ themselves define, which is checkable rather than trusted.
156
+
157
+ > **What it became.** There is no `u32` loop layer, because a cache that can go stale is worse than
158
+ > the thing it caches and this one did not need to exist. The partition is *decided* in exactly one
159
+ > place — `vgeo_mesh_wedges`, at the leaf, reading byte equality of each corner's whole record, which
160
+ > is the invariant above applied directly — and **everywhere else it travels by provenance**, the way
161
+ > `source_vertex_ids` always has: the submesh gets its wedge ids from the frontier's, the stitch
162
+ > hands out the next frontier's, and an accumulated quadric crosses each seam by an id map. A
163
+ > `BtWedgeSet` holds a wedge id per *loop* that is never rewritten, a union-find that resolves it
164
+ > after a merge, and one intrusive list per vertex. Byte equality rather than a tolerance because an
165
+ > equivalence relation has to be transitive: a tolerance is not, so grouping by one would depend on
166
+ > the order the corners were walked in and two runs over the same mesh could disagree about it.
167
+
168
+ Why values per loop rather than per wedge with an indirection: the gather and the encoder both want a
169
+ value per corner, the duplication is bounded (`3F` records against `V`, so about six times, ~108 MB on
170
+ a million-triangle primitive against the 3.7 GB the build already peaks at), and an indirection at
171
+ that seam would have to be threaded through every consumer for nothing. Why quadrics per wedge rather
172
+ than per loop: a per-loop quadric only ever accumulates its own triangle, and a fit from one triangle
173
+ is exact and says nothing. The quadric's whole value is the history, and the history belongs to the
174
+ wedge.
175
+
176
+ **Geometric quadrics stay per vertex.** They are about position, position is the vertex's, and
177
+ `ATTRIBUTE_PLAN` §3's units guard depends on that set staying untouched.
178
+
179
+ ## 4. What a collapse does
180
+
181
+ Today: the survivor's attribute record is fitted from its accumulated quadric at the new position.
182
+
183
+ With wedges, the same, once per wedge — plus the one new decision:
184
+
185
+ **When the victim's loops arrive at the survivor, which of them join a wedge that is already there?**
186
+
187
+ > **This section's answer is wrong, and the implementation does not use it.** Matching on attribute
188
+ > equality within a tolerance says that two wedges merge when their *values* agree — and two
189
+ > neighbours on a smoothly curved surface have genuinely different normals, so on a sphere it would
190
+ > refuse to merge almost every collapse and invent a crease at each one. The wedge count would grow
191
+ > without bound on exactly the content that has no seams in it at all.
192
+ >
193
+ > **What decides it is a live face.** Each face on the collapsing edge holds one corner at each
194
+ > endpoint and is about to die into the survivor, so those two corners are one corner's worth of
195
+ > surface afterwards; everything else the pair carries stays apart. Topological, exact, no tolerance
196
+ > anywhere, and it keeps every property listed below — on a smooth mesh every edge has its faces and
197
+ > every collapse merges the pair, so the whole thing still degenerates to the old behaviour exactly.
198
+ > A faceless edge is a fuse of two points rather than a simplification of a surface and is treated as
199
+ > one group; rare, and measured rather than assumed, at 6 of 6,195 collapses over a
200
+ > 12,288-triangle sphere's whole DAG.
201
+ >
202
+ > The **wedge count cannot grow** under that rule, which is what made the tolerance unnecessary
203
+ > rather than merely wrong. A vertex's wedges are the fans its creases cut its ring into; a manifold
204
+ > edge's two faces merge one pair each, so a smooth pair goes 1+1−1 = 1, a crease-to-crease pair
205
+ > 2+2−2 = 2, and a crease-to-smooth pair 2+1−1 = 2. The fan structure of the source is the ceiling,
206
+ > and the DAG still reaches one root because a cluster's triangle count keeps falling whatever its
207
+ > wedge count does.
208
+
209
+ Attribute equality, within a tolerance. Two wedges that describe the same appearance are one wedge;
210
+ two that do not stay two. This is Nanite's rule (`MeshSimplifier` matches corners by attribute
211
+ equality on a collapse) and it has the properties that matter:
212
+
213
+ - On a smooth mesh every wedge at a vertex is equal to every other, so there is exactly one, and the
214
+ whole thing degenerates to today's behaviour **exactly**. That is §6's first gate.
215
+ - A hard edge stays split as long as its two sides genuinely differ.
216
+ - It stays split *only* that long. Wedges that have converged merge, which is what stops the wedge
217
+ count from being a floor under how far the DAG can coarsen — and the builder has to reach a single
218
+ 128-triangle root.
219
+
220
+ The tolerance is per channel and has precedent in this package:
221
+ `geometry_simplify_redundant_topology` already carries `normal_cosine_tolerance = 1e-3` and
222
+ `attribute_tolerance = 1e-5` for the same question asked of a different pass.
223
+
224
+ **Merging two wedges adds their quadrics**, which is the same operation a survivor already performs
225
+ on a victim's and needs nothing new.
226
+
227
+ ## 5. Seam constraint planes
228
+
229
+ An attribute seam is a place where sliding *along* the surface destroys something the face-plane
230
+ quadric cannot see — which is the same situation an open boundary is in, and the same situation
231
+ `bt_mesh_compute_vertex_quadrics` already handles:
232
+
233
+ > for each open edge, add the plane through it perpendicular to its face
234
+ > — `add_boundary_constraints`, at `BOUNDARY_WEIGHT = 3`
235
+
236
+ An edge whose two loops on one side belong to a different wedge than the two on the other is a seam,
237
+ and it wants that same plane. This is the "boundary-plane-penalty mechanism" `VIRTUAL_GEOMETRY_DESIGN`
238
+ §2 named, reused rather than invented. It lands in §8's third stage, because it is an improvement to
239
+ where a seam vertex goes rather than a requirement for the seam to exist.
240
+
241
+ > **Not taken, and the reason is that it would be a second answer to a question already answered.**
242
+ > `ATTRIBUTE_PLAN_2026_09_18.md` put the attribute quadric's *reduction* into the placement solve and
243
+ > into the cost, so a collapse that would smear a seam is already both moved away from it and priced
244
+ > for it — in the metric, where it belongs, rather than as a penalty bolted onto the geometry. Adding
245
+ > planes to the **geometric** quadric would also inflate what `r_e` reports, and `r_e` is the one
246
+ > number the runtime's cut rule depends on; §3's units guard exists precisely to keep that set
247
+ > untouched by anything the attributes do. If a seam is later measured to drift, this is where to
248
+ > start — but it should be measured first.
249
+
250
+ ## 6. Staging
251
+
252
+ Three stages, each with a gate that can fail.
253
+
254
+ ### 6.1 — Attributes move to loops, one wedge per vertex — **DONE**
255
+
256
+ A pure refactor. Wedge id is derived from the vertex, so every vertex has exactly one wedge and
257
+ nothing about the output can change.
258
+
259
+ - `VirtualGeometryMesh.layers` are declared on `loop_data`.
260
+ - `vgeo_layers_declare` / `_like` / `_copy_vertex` become the loop-indexed equivalents.
261
+ - `vgeo_gather_faces` dedupes on `(vertex, wedge)` and returns a representative loop per output
262
+ vertex; `vgeo_encode_cluster_payload`'s third parameter is renamed to what it is.
263
+ - `vgeo_attribute_vector_{read,write}`, `bt_mesh_compute_vertex_attribute_quadrics` and the
264
+ simplifier index by wedge.
265
+
266
+ **Gate: the DAG is byte-identical**, and it very nearly is. `vgeo_build` is already proved
267
+ deterministic, so the container built before and after was hashed on five fixtures. A derived-attribute
268
+ sphere, a torus, a grid, **and a grid with authored normals and UVs** all match byte for byte. Zorah's
269
+ whole 450-primitive smoke run comes to the same 92.1 MiB it did before.
270
+
271
+ The one fixture that moves is a sphere whose source disagrees with *itself* — the wrap seam and the
272
+ poles, where two coincident vertices carry different UVs. There the weld picks an arbitrary winner
273
+ either way, and its choice moved. There was never a non-arbitrary one to preserve; that is the defect
274
+ §6.2 removes. **The seamless authored grid is what makes that claim precise rather than convenient**:
275
+ it has every channel, it goes through the same code, and it does not move.
276
+
277
+ Cost, measured on a 400,008-triangle Zorah primitive: **11.4 s to 11.9 s and 857 MiB to 934**. The
278
+ `3F` records against `V` is about six times the channel storage, arriving as 9% of a build that is
279
+ mostly not channels.
280
+
281
+ #### What it turned out to cost, that this plan did not foresee
282
+
283
+ Three things, each found by a gate rather than by reading:
284
+
285
+ 1. **A pinned vertex is *written* now, and writing it is what keeps it pinned.** Pinning meant "leave
286
+ it alone", and under per-vertex storage leaving it alone left its value alone. Under corner
287
+ storage it does not: a collapse re-points the victim's corners onto the survivor and they arrive
288
+ carrying the victim's appearance. So the carried value goes back over every corner, pinned or not,
289
+ and what pinning now means is that nothing is *derived* — no renormalize, no re-orthogonalize.
290
+ 2. **The tangent's handedness needed a vector of its own.** It is a flag no fit touches and no
291
+ derivation replaces, so a donated corner simply kept the one it arrived with. `vgeo_cut.spec.js`
292
+ caught it: one vertex of a cube-sphere came out of a cut with two different tangents in two
293
+ payloads. It now travels in a second, *verbatim* vector that the simplifier is never handed, so a
294
+ survivor's flag stays a survivor's. §4's "the survivor's flag is kept" was a sentence; it is now
295
+ a data structure.
296
+ 3. **A byte-identity gate cannot see a quadratic.** Reading a vertex's appearance means naming one of
297
+ its corners, finding one means a pass over the loop pool, and `vgeo_extract_submesh` did that per
298
+ extraction — a pass over the whole frontier repeated once per part of it. 11.4 s became 47.7 s
299
+ with the output byte-identical throughout. The frontier holds the map now
300
+ (`vgeo_frontier_loop_of_vertex`). **Every stage below needs a timing reading, not only a
301
+ correctness one.**
302
+
303
+ ### 6.2 — The source's splits become wedges — **DONE**
304
+
305
+ `vgeo_normalize_source` stops destroying them. The weld still runs and still welds *positions*; the
306
+ corners it re-points keep the values they arrived with, so a vertex the source split comes out of
307
+ normalization still split. `vgeo_layers_unify_corners` — the module written the day before purely so
308
+ the defect had a name and a line to delete — is deleted.
309
+
310
+ No loop has to remember which source vertex it came from, as this section supposed: it carries the
311
+ *values*, and `vgeo_mesh_wedges` reads the partition back off them.
312
+
313
+ - **Gate:** a box ships six normals and not a gradient. Every shipped corner normal on a subdivided
314
+ cube is within **2 degrees of a coordinate axis, at every level of the DAG**; take the split away
315
+ and it reads **54.7**, which is the diagonal of three faces averaged into one. That one number
316
+ separates the two behaviours without needing to know which face a vertex belongs to.
317
+ - **Gate:** a UV-charted box keeps each chart's coordinates on its own side of every seam — its
318
+ twelve triangles over eight points ship **24 vertices**, three per corner, one per chart. The
319
+ fixture needed fixing before it said that: with each face charted `0..1`, two of a corner's three
320
+ charts hand it the *same* coordinate, they are genuinely one wedge, and the answer is 20. Each face
321
+ into its own sixth of an atlas, as a real one would be, and it is 24.
322
+ - **Gate:** a smooth source produces exactly one wedge per vertex, asserted directly on a torus, and
323
+ 6.1's byte-identity gate still passes on four of its five fixtures.
324
+
325
+ ### 6.3 — Wedges merge through the collapse
326
+
327
+ §4's rule, plus §5's seam planes.
328
+
329
+ **6.2 and 6.3 have to land together.** Separately, 6.2 is a regression: the leaf would keep its splits
330
+ and the first group step would throw them away again, because the simplifier carries one value per
331
+ *vertex* and writes it over every corner. A DAG whose LOD 0 has hard edges and whose LOD 1 does not is
332
+ worse than one that never had them, and there is no honest place to stop between.
333
+
334
+ #### The shape the merge wants, sharpened by 6.1
335
+
336
+ - **Wedge ids are a `u32` loop layer**, assigned densely at the leaf by grouping each vertex's corners
337
+ on attribute equality. Values and quadrics are indexed by wedge.
338
+ - **Merging is per collapse, not a pass between them.** A pass at the end of a step would leave every
339
+ collapse *within* the step fitting from a quadric that never absorbed its victim's — the survivor's
340
+ wedge and the victim's would stay separate for thousands of collapses, and "conformance to the
341
+ source" is exactly the property that history carries.
342
+ - **Union-find over wedge ids** is what makes that cheap: merging is a union, the loop layer is never
343
+ rewritten, and lookups go through `find`. No corner has to be located to be re-pointed.
344
+ - **A `wedges_of_vertex` list per vertex** is what the collapse iterates: survivor's list against
345
+ victim's, matching on value within tolerance. **For a smooth mesh every list has length one**, so
346
+ the hot loop costs one comparison — which is what makes this affordable on content like Zorah,
347
+ where the wedge machinery has nothing to do.
348
+ - The tolerance has precedent in this package already: `geometry_simplify_redundant_topology` carries
349
+ `normal_cosine_tolerance = 1e-3` and `attribute_tolerance = 1e-5` for the same question asked of a
350
+ different pass.
351
+
352
+ Two passes stop being re-indexable and have to be **rewritten** rather than adjusted, and both say so
353
+ in their own source today: `vgeo_compute_vertex_tangents` (Lengyel's solve is per vertex and does not
354
+ become per corner by reading corners) and `vgeo_compute_vertex_normals` (which is where crease-angle
355
+ splitting goes, and is the whole of §1's second consequence).
356
+
357
+ - **Gate:** a hard-edged box converges to a single root — no wedge floor. It does, in 6 levels.
358
+ - **Gate:** it keeps its creases while it does — the 2-degree bound above is over *every* level, not
359
+ only the leaf.
360
+ - **Gate:** the crease-angle synthesis this unlocks. `vgeo_compute_vertex_normals` averages per
361
+ corner group, where a group is the connected run of faces a crease does not interrupt, and
362
+ `VGEO_CREASE_ANGLE` (also `VGeoBuildOptions#crease_angle`) is the threshold. It reproduces
363
+ `bt_mesh_compute_vertex_normals` step for step — Newell's normal out of a float32 scratch, summed
364
+ face-then-corner, scaled by a reciprocal — so a mesh **without** a crease comes out bit for bit
365
+ what it always did. That is not fastidiousness: without it every smooth fixture moved, for nothing,
366
+ and the gate could no longer tell a real change from a rounding one.
367
+ - **Gate:** `ATTRIBUTE_PLAN` §3's invariants all still hold. The seam identity test keys on
368
+ *position*, which is exact while every vertex has one wedge and is the wrong question the moment
369
+ one does not — a creased mesh ships two frames at one point **on purpose**. Its creased form is a
370
+ bound instead: a point of a box ships at most one frame per plane it touches, over the whole DAG,
371
+ so a fit that drifted or a level that disagreed with the level below shows up as a fourth.
372
+
373
+ #### What it cost
374
+
375
+ **Measured, on Zorah's 203 primitives of at most 5,000 triangles, every one of which built and
376
+ validated before and after.** 12.1 MiB stored becomes 13.0 (+7.4%), 14.1 MiB decoded becomes 16.5
377
+ (+17%), and the conversion runs 13.5 s against 15.3. That is the price of a hard edge and it is not
378
+ an overhead: a shipped vertex is a *wedge*, and small architectural props are mostly creases.
379
+
380
+ The clearest statement of it is the scattered-cube fixture. A cube is eight vertices and **twenty-four
381
+ shading vertices**, so 200 of them cut into 40 leaves where they cut into a dozen before — 4,800
382
+ wedges against a 128-vertex meshlet ceiling cannot be fewer than 38 clusters however well they are
383
+ packed. The DAG still closes, in 7 levels over 9 pages.
384
+
385
+ Against the *level loop* alone, with no channels changed, there is nothing to pay: paired A/B over an
386
+ 81,920-triangle sphere, alternating which tree runs first, is 2,697 ms before and 2,664 after.
387
+
388
+ ## 7. What does not change
389
+
390
+ - **The container format.** No new fields, no version move. A wedge is a builder concept; what ships
391
+ is what has always shipped, a value per cluster-local vertex.
392
+ - **The runtime.** Nothing on the device knows what a wedge is.
393
+ - **The cut rule and `r_e`.** Geometric quadrics stay per vertex and stay the only thing an error is
394
+ reported from.
395
+ - **The weld.** Positions still fuse at `weld_ratio`; connectivity is what that is for.
396
+ - **Vertex counts in the *topology*.** A wedge does not add a vertex. It adds an output vertex at
397
+ gather time, which is where a seam has always cost one.
398
+
399
+ ## 8. Rejected
400
+
401
+ - **Stop welding attribute-distinct vertices.** The obvious move and the wrong one:
402
+ `vgeo_normalize_source` already records what it costs — the clusterer shreds the mesh along every
403
+ seam, because face adjacency is read off radial cycles and two triangles across an unfused seam
404
+ look unrelated. Weld for connectivity; split for appearance. They are different questions and the
405
+ answer has to be different.
406
+ - **A per-vertex list of wedges with a loop→index.** The same thing as a wedge id on the loop, with an
407
+ extra structure to keep in step and nothing bought.
408
+ - **Wedges fixed at the leaf, never merged.** No tolerance to choose, and a floor under the DAG at
409
+ whatever the source's seam count happens to be. The builder must reach one root.
410
+ - **Deduping only at gather, with no wedge in the simplifier.** Values would travel correctly and the
411
+ quadrics would not: each loop would accumulate its own triangle and nothing else, and a fit from one
412
+ triangle is exact and worthless.
413
+
414
+ ## 9. Review ledger
415
+
416
+ ### 6.1
417
+
418
+ | # | finding | resolution |
419
+ |---|---|---|
420
+ | 1 | Moving the channels onto corners cost 4.2x the build time — `vgeo_extract_submesh` scanned the whole frontier's loop pool once per group | The frontier holds the map. 47.7 s back to 11.9. **The output was byte-identical the whole time**, which is the finding behind the finding: correctness gates are blind to this and every later stage needs a timing reading of its own. |
421
+ | 2 | A pinned vertex's corners could be a collapse's donations, so "leave it alone" stopped meaning "leave its value alone" | It is written, with the value it already had; what pinning means is that nothing is *derived* for it. |
422
+ | 3 | The tangent's handedness is fitted by nothing and derived by nothing, so a donated corner kept the flag it arrived with — and a cut disagreed with itself | A second, verbatim vector the simplifier is never handed. Caught by `vgeo_cut.spec.js`, which existed. |
423
+ | 4 | `vgeo_layers_unify_corners` claimed two call sites | One. The collapse path stopped needing it when 2 landed, and the doc had not caught up. |
424
+ | 5 | The layer-declaration comment still talked about vertices | Loops, and three records per triangle rather than one per vertex. |
425
+ | 6 | `vgeo_stitch_frontier`'s comment claimed "a handful of topologies" | One per group plus the frontier, which sums to a pass over the round. The cost was right; the description was not. |
426
+
427
+ `check-types` at budget: declarations emit clean, the consumer program reports 19, all in the
428
+ documented set, none in a file this touched. Zorah's smoke: 450 of 450, every container audited,
429
+ 92.1 MiB — the same byte count as before.
430
+
431
+ ### 6.2 and 6.3
432
+
433
+ | # | finding | resolution |
434
+ |---|---|---|
435
+ | 1 | §4's merge rule is wrong. Matching wedges on **attribute equality within a tolerance** means two neighbours on a curved surface — whose normals genuinely differ — refuse to merge, so a sphere would gain a crease at every collapse and the wedge count would grow without bound on content with no seams at all | A live face decides it instead: each face on the collapsing edge holds one corner at each endpoint and is about to die into the survivor, so those two are one corner's worth of surface. Topological, exact, no tolerance anywhere. And it made the tolerance *unnecessary* as well as wrong — the fan arithmetic in §4 shows the wedge count cannot grow, which was the thing the tolerance was there to prevent. |
436
+ | 2 | §3's `u32` wedge-id loop layer is a cache of the values, and a cache that can go stale | Deleted before it was written. The partition is decided in exactly one place, from the values themselves, and travels by provenance everywhere else — the way `source_vertex_ids` always has. Byte equality rather than a tolerance, because an equivalence relation must be transitive or the partition depends on the order corners were walked in. |
437
+ | 3 | **A set that is not maintained is a set whose ids go stale.** The wedge set lived inside the `attributes` bundle, so a mesh with no channels carried none, and a victim's loops arrived at the survivor still naming the victim's wedge | It is its own argument to the simplifier and is merged whether or not values ride on it. **The byte-identity gate saw nothing**: the containers matched throughout, and what caught it was an unrelated ceiling assertion — clusters of 132 vertices against a cap of 128. |
438
+ | 4 | Two ceilings counted the wrong thing. `VGeoFaceGraph.count_unique_vertices` and the bin packer's `unique_vertices` both fed the meshlet's 128-vertex limit a count of **mesh vertices**, where a meshlet carries one record per wedge | Both count wedges. Found by a hard-edged fixture at 240 against 128 — and the two had to be fixed separately, because the second is the packer's own and the first only bounds the pieces going into it. |
439
+ | 5 | The crease pass reimplemented the face normal as a cross product, so every smooth fixture moved for no reason and the gate could no longer tell a real change from a rounding one | It reproduces `bt_mesh_compute_vertex_normals` step for step: Newell's normal out of a float32 scratch, summed face-then-corner, scaled by a reciprocal rather than divided. Four of the five fixtures went back to byte-identical, and the one that moves is the one with a real seam in it. |
440
+ | 6 | The UV-chart gate asserted 24 shipped vertices and got 20 | The fixture, not the builder. With every face charted `0..1`, two of a corner's three charts hand it the same coordinate — they *are* one wedge and 20 is right. Each face into its own sixth of an atlas, as a real one would be. |
441
+ | 7 | The seam identity test keys on position, which a creased mesh is entitled to break: two frames at one point is what a hard edge **is** | Said so where the test is, and asked the creased form of the question separately — a point of a box ships at most one frame per plane it touches, over the whole DAG. |
442
+ | 8 | The scattered-cube test expected fewer than 40 leaves and got exactly 40 | Not a regression to hide: a cube is eight vertices and twenty-four shading vertices, and 4,800 wedges cannot pack below 38 clusters. The expectation carries the arithmetic now. |
443
+ | 9 | **A third place counting the wrong thing**, found by going looking for it after the first two: `vgeo_build_group_step`'s overshoot heuristic divides `topology.vertices.count()` by the meshlet vertex ceiling to decide how far to tighten its target | `bt_wedges_live_count`. Only a heuristic — the gate is the re-cut's own cluster count — but one that reads low at every crease, so the step aims at a target it has already missed and can give up a group it could have simplified. The lesson is the pattern rather than the line: **every ceiling that says "vertices" means wedges**, and there were three. |
444
+ | 10 | The cost model and the collapse disagreed about a faceless edge. `reduce_pair_attributes` unioned *everything* into one group; `carry_wedges` merged only the victim's wedges into the survivor's first, leaving the survivor's others alone | The cost model follows the collapse. It is 0.1% of collapses and a heuristic either way, but a price that is not the price of the collapse that happens is a bug waiting for someone to trust it. |
445
+ | 11 | **A timing harness read a 1.57x regression that did not exist.** It built its sphere by hand and never called `vgeo_normalize_source`, so the mesh kept its raw wrap seam and its degenerate poles — and the two trees then built *different DAGs*, 39 levels against 19. It was comparing two different builds and reporting the difference as a cost | Through the real path: **13 levels and 1,710 clusters on both**, median 2,294 ms against 2,405, and identical retained heap. Which is the same +3 to 5% the Zorah conversion measures over 3,095,102 triangles. The rule the §6.1 review wrote — take a timing reading at every stage — needs a second half: *take it through the pipeline the asset goes through*, or the reading is of something else. |
446
+
447
+ `check-types` at budget: declarations emit clean, the consumer program reports 19, all in the
448
+ documented set, none in a file this touched.
@@ -6,20 +6,36 @@
6
6
  */
7
7
  export class VGeoBuildOptions {
8
8
  /**
9
- * Which codec page blobs are stored under — {@link VGEO_CODEC_RAW} or {@link VGEO_CODEC_LZ4}.
10
- * Compressed by default, and there is no case where turning it off makes a file smaller: the
11
- * encoder stores a page raw whenever compressing it would not shrink it, per page.
9
+ * Which codec page blobs are stored under — {@link VGEO_CODEC_RAW}, {@link VGEO_CODEC_LZ4} or
10
+ * {@link VGEO_CODEC_DEFLATE}. Compressed by default, and there is no case where turning it off
11
+ * makes a file smaller: the encoder stores a page raw whenever compressing it would not shrink
12
+ * it, per page.
12
13
  *
13
- * What it costs a reader is one LZ4 block decode per page installed, into the buffer the page
14
- * was going to be copied into anyway. What it saves is transfer and disk on every fetch of
15
- * every page, forever.
14
+ * What it costs a reader is one block decode per page installed, into the buffer the page was
15
+ * going to be copied into anyway. What it saves is transfer and disk on every fetch of every
16
+ * page, forever.
17
+ *
18
+ * **Deflate, because a build happens once and a fetch happens forever.** It is the denser of
19
+ * the two compressing codecs — 1.369x against LZ4's 1.295x over the pages of a 240x120 torus,
20
+ * 1.48x against 1.38x over a 6,016-triangle sphere — and the bytes it saves are saved on every
21
+ * fetch of every page by every client, for the life of the asset. What it costs is decode:
22
+ * 126 MB/s of decoded output against LZ4's 562, about four and a half times, and that lands on
23
+ * the residency path where it is the arrival cost of a page. `VGEO_CODEC_LZ4` is the setting for a
24
+ * container whose pages arrive while someone is looking at it, and it is one line away.
25
+ *
26
+ * Choosing it here is a choice about *readers*, not about the format: a codec id is a per-asset
27
+ * capability gate (`VGEO_FORMAT.md` §5), so a container built under this default opens only in
28
+ * a reader that carries the deflate decoder. `format_version` does not move, and an older
29
+ * reader refuses the asset rather than misreading it.
16
30
  *
17
31
  * @type {number}
18
32
  */
19
33
  codec: number;
20
34
  /**
21
35
  * How hard the codec looks for matches, from {@link LZ4_LEVEL}. Nine by default, which is the
22
- * top of the reference's hash-chain levels.
36
+ * top of the reference's hash-chain levels. **LZ4 only, and the default codec is not LZ4** —
37
+ * `CompressionStream` exposes no level, so this does nothing at all unless {@link codec} was
38
+ * set to {@link VGEO_CODEC_LZ4}.
23
39
  *
24
40
  * **It changes nothing about the file.** Every level writes the same LZ4 block format, so a
25
41
  * container built at any of them reads in every build of the reader that exists; the level is
@@ -81,6 +97,27 @@ export class VGeoBuildOptions {
81
97
  * @type {number}
82
98
  */
83
99
  nominal_page_size: number;
100
+ /**
101
+ * How much a full unit of each shading channel is worth in object-space position error, as a
102
+ * **fraction of the geometry's own bounding-box diagonal** — one entry per
103
+ * {@link VGEO_ATTRIBUTES} channel, position's ignored.
104
+ *
105
+ * This is the number that makes the simplifier's metric mean anything. An attribute's gradient
106
+ * is *attribute per length*, so the error it contributes is dimensionless while the geometric
107
+ * error beside it is a squared length; adding them without saying how they compare would let the
108
+ * arbitrary range a channel happens to use decide how much the surface is allowed to move. The
109
+ * weight is that statement, and {@link vgeo_resolve_attribute_weights} is where it is made.
110
+ *
111
+ * A ratio rather than a distance for the same reason {@link weld_ratio} is: a conversion is many
112
+ * geometries at many sizes, and a fixed distance right for a doorknob is wrong for a cathedral.
113
+ *
114
+ * The defaults are {@link VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS}, which says what they were
115
+ * measured against. Raising one buys fidelity in that channel at the price of geometric error,
116
+ * and the exchange rate is exactly what the number says it is.
117
+ *
118
+ * @type {number[]}
119
+ */
120
+ attribute_weight_ratios: number[];
84
121
  /**
85
122
  * @type {VGeoLevelOptions}
86
123
  */
@@ -1 +1 @@
1
- {"version":3,"file":"VGeoBuildOptions.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH;IACI;;;;;;;;;;OAUG;IACH,OAFU,MAAM,CAEO;IAEvB;;;;;;;;;;;;;;;;OAgBG;IACH,aAFU,MAAM,CAEmB;IAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,YAFU,MAAM,CAEE;IAElB;;;;;;;;;;;;;;OAcG;IACH,mBAFU,MAAM,CAE2B;IAE3C;;OAEG;IACH,eAFU,gBAAgB,CAEa;CAC1C;iCA7FgC,6BAA6B"}
1
+ {"version":3,"file":"VGeoBuildOptions.d.ts","sourceRoot":"","sources":["../../../../../../../src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH;IACI;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAFU,MAAM,CAEW;IAE3B;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAFU,MAAM,CAEmB;IAEnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4BG;IACH,YAFU,MAAM,CAEE;IAElB;;;;;;;;;;;;;;OAcG;IACH,mBAFU,MAAM,CAE2B;IAE3C;;;;;;;;;;;;;;;;;;;OAmBG;IACH,yBAFU,MAAM,EAAE,CAEqD;IAEvE;;OAEG;IACH,eAFU,gBAAgB,CAEa;CAC1C;iCAnIgC,6BAA6B"}
@@ -1,5 +1,6 @@
1
1
  import { LZ4_LEVEL } from "../../../../../core/binary/lz4/LZ4_LEVEL.js";
2
- import { VGEO_CODEC_LZ4 } from "../format/frame/VGEO_CODEC_LZ4.js";
2
+ import { VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS } from "./mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js";
3
+ import { VGEO_CODEC_DEFLATE } from "../format/frame/VGEO_CODEC_DEFLATE.js";
3
4
  import { VGEO_DEFAULT_PAGE_SIZE } from "../format/page/VGEO_DEFAULT_PAGE_SIZE.js";
4
5
  import { VGeoLevelOptions } from "./level/VGeoLevelOptions.js";
5
6
 
@@ -11,21 +12,37 @@ import { VGeoLevelOptions } from "./level/VGeoLevelOptions.js";
11
12
  */
12
13
  export class VGeoBuildOptions {
13
14
  /**
14
- * Which codec page blobs are stored under — {@link VGEO_CODEC_RAW} or {@link VGEO_CODEC_LZ4}.
15
- * Compressed by default, and there is no case where turning it off makes a file smaller: the
16
- * encoder stores a page raw whenever compressing it would not shrink it, per page.
15
+ * Which codec page blobs are stored under — {@link VGEO_CODEC_RAW}, {@link VGEO_CODEC_LZ4} or
16
+ * {@link VGEO_CODEC_DEFLATE}. Compressed by default, and there is no case where turning it off
17
+ * makes a file smaller: the encoder stores a page raw whenever compressing it would not shrink
18
+ * it, per page.
17
19
  *
18
- * What it costs a reader is one LZ4 block decode per page installed, into the buffer the page
19
- * was going to be copied into anyway. What it saves is transfer and disk on every fetch of
20
- * every page, forever.
20
+ * What it costs a reader is one block decode per page installed, into the buffer the page was
21
+ * going to be copied into anyway. What it saves is transfer and disk on every fetch of every
22
+ * page, forever.
23
+ *
24
+ * **Deflate, because a build happens once and a fetch happens forever.** It is the denser of
25
+ * the two compressing codecs — 1.369x against LZ4's 1.295x over the pages of a 240x120 torus,
26
+ * 1.48x against 1.38x over a 6,016-triangle sphere — and the bytes it saves are saved on every
27
+ * fetch of every page by every client, for the life of the asset. What it costs is decode:
28
+ * 126 MB/s of decoded output against LZ4's 562, about four and a half times, and that lands on
29
+ * the residency path where it is the arrival cost of a page. `VGEO_CODEC_LZ4` is the setting for a
30
+ * container whose pages arrive while someone is looking at it, and it is one line away.
31
+ *
32
+ * Choosing it here is a choice about *readers*, not about the format: a codec id is a per-asset
33
+ * capability gate (`VGEO_FORMAT.md` §5), so a container built under this default opens only in
34
+ * a reader that carries the deflate decoder. `format_version` does not move, and an older
35
+ * reader refuses the asset rather than misreading it.
21
36
  *
22
37
  * @type {number}
23
38
  */
24
- codec = VGEO_CODEC_LZ4;
39
+ codec = VGEO_CODEC_DEFLATE;
25
40
 
26
41
  /**
27
42
  * How hard the codec looks for matches, from {@link LZ4_LEVEL}. Nine by default, which is the
28
- * top of the reference's hash-chain levels.
43
+ * top of the reference's hash-chain levels. **LZ4 only, and the default codec is not LZ4** —
44
+ * `CompressionStream` exposes no level, so this does nothing at all unless {@link codec} was
45
+ * set to {@link VGEO_CODEC_LZ4}.
29
46
  *
30
47
  * **It changes nothing about the file.** Every level writes the same LZ4 block format, so a
31
48
  * container built at any of them reads in every build of the reader that exists; the level is
@@ -90,6 +107,28 @@ export class VGeoBuildOptions {
90
107
  */
91
108
  nominal_page_size = VGEO_DEFAULT_PAGE_SIZE;
92
109
 
110
+ /**
111
+ * How much a full unit of each shading channel is worth in object-space position error, as a
112
+ * **fraction of the geometry's own bounding-box diagonal** — one entry per
113
+ * {@link VGEO_ATTRIBUTES} channel, position's ignored.
114
+ *
115
+ * This is the number that makes the simplifier's metric mean anything. An attribute's gradient
116
+ * is *attribute per length*, so the error it contributes is dimensionless while the geometric
117
+ * error beside it is a squared length; adding them without saying how they compare would let the
118
+ * arbitrary range a channel happens to use decide how much the surface is allowed to move. The
119
+ * weight is that statement, and {@link vgeo_resolve_attribute_weights} is where it is made.
120
+ *
121
+ * A ratio rather than a distance for the same reason {@link weld_ratio} is: a conversion is many
122
+ * geometries at many sizes, and a fixed distance right for a doorknob is wrong for a cathedral.
123
+ *
124
+ * The defaults are {@link VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS}, which says what they were
125
+ * measured against. Raising one buys fidelity in that channel at the price of geometric error,
126
+ * and the exchange rate is exactly what the number says it is.
127
+ *
128
+ * @type {number[]}
129
+ */
130
+ attribute_weight_ratios = VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.slice();
131
+
93
132
  /**
94
133
  * @type {VGeoLevelOptions}
95
134
  */