@woosh/meep-engine 3.27.0 → 3.29.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 (648) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/binary/BitSet.d.ts +16 -0
  4. package/src/core/binary/BitSet.d.ts.map +1 -1
  5. package/src/core/binary/BitSet.js +159 -49
  6. package/src/core/binary/allocator/OffsetAllocator.d.ts +48 -0
  7. package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
  8. package/src/core/binary/allocator/OffsetAllocator.js +208 -9
  9. package/src/core/binary/compression/compress_bytes.d.ts +20 -0
  10. package/src/core/binary/compression/compress_bytes.d.ts.map +1 -0
  11. package/src/core/binary/compression/compress_bytes.js +35 -0
  12. package/src/core/binary/compression/decompress_bytes_into.d.ts +30 -0
  13. package/src/core/binary/compression/decompress_bytes_into.d.ts.map +1 -0
  14. package/src/core/binary/compression/decompress_bytes_into.js +87 -0
  15. package/src/core/binary/compression/deflate_decompress_bound.d.ts +27 -0
  16. package/src/core/binary/compression/deflate_decompress_bound.d.ts.map +1 -0
  17. package/src/core/binary/compression/deflate_decompress_bound.js +32 -0
  18. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts +15 -0
  19. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts.map +1 -0
  20. package/src/core/geom/3d/mat4/m4_linear_determinant.js +24 -0
  21. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts +4 -2
  22. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts.map +1 -1
  23. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +21 -11
  24. package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts +24 -0
  25. package/src/core/geom/3d/quadric/quadric_attribute_add.d.ts.map +1 -0
  26. package/src/core/geom/3d/quadric/quadric_attribute_add.js +31 -0
  27. package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts +46 -0
  28. package/src/core/geom/3d/quadric/quadric_attribute_reduce.d.ts.map +1 -0
  29. package/src/core/geom/3d/quadric/quadric_attribute_reduce.js +104 -0
  30. package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts +62 -0
  31. package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.d.ts.map +1 -0
  32. package/src/core/geom/3d/quadric/quadric_attribute_set_from_triangle.js +158 -0
  33. package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts +21 -0
  34. package/src/core/geom/3d/quadric/quadric_attribute_size.d.ts.map +1 -0
  35. package/src/core/geom/3d/quadric/quadric_attribute_size.js +24 -0
  36. package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts +34 -0
  37. package/src/core/geom/3d/quadric/quadric_attribute_solve.d.ts.map +1 -0
  38. package/src/core/geom/3d/quadric/quadric_attribute_solve.js +55 -0
  39. package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts +28 -0
  40. package/src/core/geom/3d/quadric/quadric_attribute_sub.d.ts.map +1 -0
  41. package/src/core/geom/3d/quadric/quadric_attribute_sub.js +35 -0
  42. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts +15 -0
  43. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts.map +1 -1
  44. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +35 -0
  45. package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts +93 -0
  46. package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.d.ts.map +1 -0
  47. package/src/core/geom/3d/topology/struct/binary/BtWedgeSet.js +128 -0
  48. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts +38 -0
  49. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.d.ts.map +1 -0
  50. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_compute_wedge_attribute_quadrics.js +113 -0
  51. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts +47 -1
  52. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.d.ts.map +1 -1
  53. package/src/core/geom/3d/topology/struct/binary/io/bt_mesh_simplify.js +677 -8
  54. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts +22 -0
  55. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.d.ts.map +1 -0
  56. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_live_count.js +46 -0
  57. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts +19 -0
  58. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.d.ts.map +1 -0
  59. package/src/core/geom/3d/topology/struct/binary/io/bt_wedges_per_vertex.js +49 -0
  60. package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts +63 -0
  61. package/src/core/graph/layout/layered/BoxLayoutOptions.d.ts.map +1 -0
  62. package/src/core/graph/layout/layered/BoxLayoutOptions.js +80 -0
  63. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.d.ts +35 -0
  64. package/src/core/graph/layout/layered/LayoutBoxGraph.d.ts.map +1 -0
  65. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/LayoutBoxGraph.js +53 -1
  66. package/src/core/graph/layout/layered/layout_assign_coordinates.d.ts.map +1 -0
  67. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.js +72 -68
  68. package/src/core/graph/layout/layered/layout_assign_layers.d.ts.map +1 -0
  69. package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.js +4 -2
  70. package/src/core/graph/layout/layered/layout_break_cycles.d.ts.map +1 -0
  71. package/src/core/graph/layout/layered/layout_insert_lanes.d.ts.map +1 -0
  72. package/src/core/graph/layout/layered/layout_order_layers.d.ts.map +1 -0
  73. package/src/core/graph/layout/layered/layout_orient_links.d.ts.map +1 -0
  74. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts +7 -51
  75. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.d.ts.map +1 -1
  76. package/src/core/model/node-graph/visual/layout/NodeGraphLayoutOptions.js +8 -69
  77. package/src/core/model/node-graph/visual/layout/layout_build_problem.js +1 -1
  78. package/src/core/model/node-graph/visual/layout/layout_node_graph.js +6 -6
  79. package/src/core/model/node-graph/visual/layout/route_grid_build.js +1 -1
  80. package/src/engine/Engine.d.ts.map +1 -1
  81. package/src/engine/Engine.js +38 -1
  82. package/src/engine/graphics3/DebugDrawSystem.d.ts +7 -5
  83. package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
  84. package/src/engine/graphics3/DebugDrawSystem.js +206 -203
  85. package/src/engine/graphics3/DecalSystem.d.ts +4 -5
  86. package/src/engine/graphics3/DecalSystem.d.ts.map +1 -1
  87. package/src/engine/graphics3/DecalSystem.js +5 -5
  88. package/src/engine/graphics3/FogOfWarSystem.d.ts +7 -12
  89. package/src/engine/graphics3/FogOfWarSystem.d.ts.map +1 -1
  90. package/src/engine/graphics3/FogOfWarSystem.js +8 -12
  91. package/src/engine/graphics3/GraphicsEngine.d.ts +5 -1
  92. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  93. package/src/engine/graphics3/GraphicsEngine.js +834 -830
  94. package/src/engine/graphics3/prefab/prefab_instantiate.js +230 -230
  95. package/src/shade/RENDERER_CONTRACT.md +31 -17
  96. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +1 -1
  97. package/src/shade/playground/caldera/README.md +529 -0
  98. package/src/shade/playground/caldera/caldera_format.d.ts +151 -0
  99. package/src/shade/playground/caldera/caldera_format.d.ts.map +1 -0
  100. package/src/shade/playground/caldera/caldera_format.js +241 -0
  101. package/src/shade/playground/caldera/index.html +126 -0
  102. package/src/shade/playground/caldera/main.d.ts.map +1 -0
  103. package/src/shade/playground/caldera/main.js +1565 -0
  104. package/src/shade/playground/mesh_row_ceiling/README.md +85 -0
  105. package/src/shade/playground/mesh_row_ceiling/index.html +31 -0
  106. package/src/shade/playground/mesh_row_ceiling/main.d.ts +2 -0
  107. package/src/shade/playground/mesh_row_ceiling/main.d.ts.map +1 -0
  108. package/src/shade/playground/mesh_row_ceiling/main.js +389 -0
  109. package/src/shade/playground/vgeo_runtime/README.md +32 -16
  110. package/src/shade/playground/vgeo_runtime/main.js +21 -7
  111. package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.d.ts +3 -3
  112. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -0
  113. package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.js +12 -11
  114. package/src/shade/playground/vgeo_scene/README.md +487 -0
  115. package/src/shade/playground/vgeo_scene/index.html +144 -0
  116. package/src/shade/playground/vgeo_scene/main.d.ts +2 -0
  117. package/src/shade/playground/vgeo_scene/main.d.ts.map +1 -0
  118. package/src/shade/playground/vgeo_scene/main.js +2217 -0
  119. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  120. package/src/shade/renderer/GraphicsContext.js +6 -0
  121. package/src/shade/renderer/Renderer.d.ts +6 -1
  122. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  123. package/src/shade/renderer/Renderer.js +28 -2
  124. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  125. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +100 -8
  126. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +70 -35
  127. package/src/shade/renderer/extension/RenderExtension.d.ts +15 -18
  128. package/src/shade/renderer/extension/RenderExtension.d.ts.map +1 -1
  129. package/src/shade/renderer/extension/RenderExtension.js +15 -19
  130. package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts +5 -5
  131. package/src/shade/renderer/extension/RenderExtensionRegistry.d.ts.map +1 -1
  132. package/src/shade/renderer/extension/RenderExtensionRegistry.js +29 -166
  133. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  134. package/src/shade/renderer/geometry/GPUGeometryManager.js +73 -16
  135. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  136. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +18 -3
  137. package/src/shade/renderer/geometry/meshlet/COMPACTION_PLAN_2026_09_16.md +663 -0
  138. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +100 -5
  139. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +688 -81
  141. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  142. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +3 -29
  143. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts +22 -0
  144. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts.map +1 -0
  145. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.js +49 -0
  146. package/src/shade/renderer/geometry/virtual/ATTRIBUTE_PLAN_2026_09_18.md +540 -0
  147. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +159 -66
  148. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +40 -21
  149. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +15 -14
  150. package/src/shade/renderer/geometry/virtual/WEDGE_PLAN_2026_09_18.md +358 -0
  151. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +58 -8
  152. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  153. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +64 -10
  154. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.d.ts.map +1 -1
  155. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +5 -9
  156. package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts +40 -0
  157. package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.d.ts.map +1 -0
  158. package/src/shade/renderer/geometry/virtual/build/level/VGeoContribution.js +24 -0
  159. package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts +91 -0
  160. package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.d.ts.map +1 -0
  161. package/src/shade/renderer/geometry/virtual/build/level/VGeoFrontier.js +91 -0
  162. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts +24 -0
  163. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.d.ts.map +1 -0
  164. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupRepack.js +25 -0
  165. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts +77 -0
  166. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.d.ts.map +1 -0
  167. package/src/shade/renderer/geometry/virtual/build/level/VGeoGroupStep.js +84 -0
  168. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts.map +1 -0
  169. package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.d.ts → level/VGeoLevelOptions.d.ts} +19 -6
  170. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -0
  171. package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.js → level/VGeoLevelOptions.js} +20 -7
  172. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts.map +1 -0
  173. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts +19 -0
  174. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.d.ts.map +1 -0
  175. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster.js +58 -0
  176. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts +18 -0
  177. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.d.ts.map +1 -0
  178. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_cluster_graph.js +116 -0
  179. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts +18 -0
  180. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.d.ts.map +1 -0
  181. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_group_step.js +545 -0
  182. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts +16 -0
  183. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.d.ts.map +1 -0
  184. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_leaf_frontier.js +110 -0
  185. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +11 -0
  186. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -0
  187. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.js +308 -0
  188. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.d.ts → level/vgeo_build_root_group.d.ts} +1 -1
  189. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts.map +1 -0
  190. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.js → level/vgeo_build_root_group.js} +1 -1
  191. package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts +26 -0
  192. package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.d.ts.map +1 -0
  193. package/src/shade/renderer/geometry/virtual/build/level/vgeo_compute_group_boundary.js +123 -0
  194. package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts +26 -0
  195. package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.d.ts.map +1 -0
  196. package/src/shade/renderer/geometry/virtual/build/level/vgeo_error_bands.js +78 -0
  197. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts +24 -0
  198. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.d.ts.map +1 -0
  199. package/src/shade/renderer/geometry/virtual/build/level/vgeo_partition_into_groups.js +337 -0
  200. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts +29 -0
  201. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.d.ts.map +1 -0
  202. package/src/shade/renderer/geometry/virtual/build/level/vgeo_run_round.js +147 -0
  203. package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts +37 -0
  204. package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.d.ts.map +1 -0
  205. package/src/shade/renderer/geometry/virtual/build/level/vgeo_stitch_frontier.js +243 -0
  206. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts +29 -0
  207. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.d.ts.map +1 -0
  208. package/src/shade/renderer/geometry/virtual/build/level/vgeo_try_repack.js +117 -0
  209. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts +46 -0
  210. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.d.ts.map +1 -0
  211. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_CREASE_ANGLE.js +45 -0
  212. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts +64 -0
  213. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.d.ts.map +1 -0
  214. package/src/shade/renderer/geometry/virtual/build/mesh/VGEO_DEFAULT_ATTRIBUTE_WEIGHT_RATIOS.js +68 -0
  215. package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.d.ts → mesh/VirtualGeometryMesh.d.ts} +13 -8
  216. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -0
  217. package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.js → mesh/VirtualGeometryMesh.js} +40 -18
  218. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts +58 -0
  219. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.d.ts.map +1 -0
  220. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_layout.js +79 -0
  221. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts +39 -0
  222. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.d.ts.map +1 -0
  223. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_read.js +68 -0
  224. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts +53 -0
  225. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.d.ts.map +1 -0
  226. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_attribute_vector_write.js +243 -0
  227. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +45 -0
  228. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -0
  229. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.js +300 -0
  230. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_tangents.d.ts → mesh/vgeo_compute_vertex_tangents.d.ts} +12 -8
  231. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -0
  232. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.js +151 -0
  233. package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.d.ts → mesh/vgeo_normalize_source.d.ts} +8 -1
  234. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -0
  235. package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.js → mesh/vgeo_normalize_source.js} +27 -12
  236. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts +38 -0
  237. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.d.ts.map +1 -0
  238. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_resolve_attribute_weights.js +81 -0
  239. package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.d.ts → page/vgeo_assemble_pages.d.ts} +1 -1
  240. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts.map +1 -0
  241. package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.js → page/vgeo_assemble_pages.js} +9 -9
  242. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.d.ts → page/vgeo_build_page_nodes.d.ts} +1 -1
  243. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts.map +1 -0
  244. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.js → page/vgeo_build_page_nodes.js} +4 -4
  245. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts.map +1 -0
  246. package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.js → page/vgeo_page_node_count.js} +2 -2
  247. package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.d.ts → partition/VGeoFaceGraph.d.ts} +23 -5
  248. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -0
  249. package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.js → partition/VGeoFaceGraph.js} +28 -21
  250. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -0
  251. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts.map +1 -0
  252. package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.js → partition/vgeo_partition_graph.js} +2 -2
  253. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts +41 -0
  254. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts.map +1 -0
  255. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.js +92 -0
  256. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts.map +1 -0
  257. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.js → partition/vgeo_split_face_set.js} +13 -56
  258. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -0
  259. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.js +371 -0
  260. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts.map +1 -0
  261. package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.js → sphere/vgeo_bounding_sphere.js} +3 -3
  262. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts.map +1 -0
  263. package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.js → sphere/vgeo_containing_sphere.js} +3 -3
  264. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts.map +1 -0
  265. package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.js → sphere/vgeo_sphere_inflate_to_contain.js} +1 -1
  266. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +39 -0
  267. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -0
  268. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.js +42 -0
  269. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +37 -0
  270. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -0
  271. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.js +39 -0
  272. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +23 -0
  273. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -0
  274. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.js +135 -0
  275. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +24 -0
  276. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -0
  277. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.js +95 -0
  278. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts.map +1 -0
  279. package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.js → submesh/vgeo_narrow_indices.js} +3 -3
  280. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts +6 -2
  281. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  282. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +37 -16
  283. package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts +32 -0
  284. package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.d.ts.map +1 -0
  285. package/src/shade/renderer/geometry/virtual/build/vgeo_subtree_bounds.js +93 -0
  286. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +20 -6
  287. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  288. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +22 -8
  289. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts +8 -2
  290. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts.map +1 -1
  291. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +8 -2
  292. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts +16 -0
  293. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_corner.d.ts.map +1 -0
  294. package/src/shade/renderer/geometry/virtual/format/attribute/{vgeo_layers_copy_vertex.js → vgeo_layers_copy_corner.js} +8 -8
  295. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts +18 -7
  296. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.d.ts.map +1 -1
  297. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_declare.js +19 -8
  298. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.d.ts +3 -3
  299. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_like.js +3 -3
  300. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts +27 -0
  301. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.d.ts.map +1 -0
  302. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_mesh_wedges.js +97 -0
  303. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts +7 -3
  304. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.d.ts.map +1 -1
  305. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET.js +21 -11
  306. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts +21 -0
  307. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.d.ts.map +1 -0
  308. package/src/shade/renderer/geometry/virtual/format/cluster/VGEO_CLUSTER_OFFSET_V4.js +41 -0
  309. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts +19 -1
  310. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.d.ts.map +1 -1
  311. package/src/shade/renderer/geometry/virtual/format/dag/VirtualGeometryCluster.js +20 -1
  312. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts +25 -0
  313. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.d.ts.map +1 -0
  314. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_DEFLATE.js +24 -0
  315. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts +5 -0
  316. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.d.ts.map +1 -1
  317. package/src/shade/renderer/geometry/virtual/format/frame/VGEO_CODEC_LZ4.js +5 -0
  318. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.d.ts.map +1 -1
  319. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_codec_name.js +5 -0
  320. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts +10 -3
  321. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.d.ts.map +1 -1
  322. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_decode_frame_blob.js +37 -3
  323. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts +21 -13
  324. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.d.ts.map +1 -1
  325. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_encode_frame_blob.js +37 -13
  326. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts +8 -2
  327. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.d.ts.map +1 -1
  328. package/src/shade/renderer/geometry/virtual/format/frame/vgeo_frame_decode_bound.js +14 -2
  329. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts +18 -0
  330. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.d.ts.map +1 -1
  331. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FORMAT_VERSION.js +19 -1
  332. package/src/shade/renderer/geometry/virtual/format/header/VGEO_HEADER_OFFSET.js +6 -0
  333. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts +21 -0
  334. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.d.ts.map +1 -0
  335. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MAX_LEVEL_COUNT.js +20 -0
  336. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts +9 -0
  337. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.d.ts.map +1 -1
  338. package/src/shade/renderer/geometry/virtual/format/header/VGEO_MIN_READER_VERSION.js +10 -1
  339. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts +15 -0
  340. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts.map +1 -0
  341. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js +15 -0
  342. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.d.ts +1 -2
  343. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET.js +10 -3
  344. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts +20 -0
  345. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.d.ts.map +1 -0
  346. package/src/shade/renderer/geometry/virtual/format/page/VGEO_PAGE_OFFSET_V5.js +43 -0
  347. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +32 -2
  348. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
  349. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +15 -7
  350. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  351. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +2 -2
  352. package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts +6 -4
  353. package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.d.ts.map +1 -1
  354. package/src/shade/renderer/geometry/virtual/format/read/VGeoDirectory.js +6 -4
  355. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +13 -2
  356. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  357. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +13 -2
  358. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts +5 -1
  359. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.d.ts.map +1 -1
  360. package/src/shade/renderer/geometry/virtual/format/read/VGeoPageFetcherCore.js +14 -1
  361. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.d.ts.map +1 -1
  362. package/src/shade/renderer/geometry/virtual/format/read/vgeo_page_worker_serve.js +1 -0
  363. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts +4 -1
  364. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.d.ts.map +1 -1
  365. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_directory.js +53 -11
  366. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
  367. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +7 -3
  368. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts +12 -2
  369. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  370. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +120 -37
  371. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts +6 -2
  372. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  373. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +130 -47
  374. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts +2 -2
  375. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  376. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +60 -22
  377. package/src/shade/renderer/geometry/virtual/runtime/CULL_PLAN_2026_09_16.md +385 -0
  378. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +21 -2
  379. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +52 -2
  380. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  381. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +97 -1
  382. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +4 -4
  383. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  384. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +10 -0
  385. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.d.ts.map +1 -1
  386. package/src/shade/renderer/geometry/virtual/runtime/VGeoPageFetcher.js +9 -0
  387. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
  388. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +59 -34
  389. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +8 -8
  390. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  391. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +45 -88
  392. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts +19 -0
  393. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -0
  394. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +116 -0
  395. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts +31 -0
  396. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -0
  397. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +99 -0
  398. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts +17 -0
  399. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts.map +1 -0
  400. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js +39 -0
  401. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -1
  402. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +7 -63
  403. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  404. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +13 -30
  405. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  406. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +53 -71
  407. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +1 -0
  408. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +22 -4
  409. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +7 -2
  410. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -1
  411. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +8 -3
  412. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +1 -0
  413. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +10 -0
  414. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +1 -1
  415. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +13 -11
  416. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -1
  417. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +13 -11
  418. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -1
  419. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +4 -0
  420. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.d.ts.map +1 -1
  421. package/src/shade/renderer/global_illumination/sharc/shader/shader_sharc_evict.js +3 -4
  422. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts +6 -2
  423. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.d.ts.map +1 -1
  424. package/src/shade/renderer/gpu_primitive/indirect/IndirectComputeCommandBuildPass.js +90 -12
  425. package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts +22 -0
  426. package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.d.ts.map +1 -0
  427. package/src/shade/renderer/gpu_primitive/indirect/WEBGPU_GUARANTEED_MAX_COMPUTE_WORKGROUPS_PER_DIMENSION.js +21 -0
  428. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  429. package/src/shade/renderer/loader/gltf/load_gltf.js +0 -19
  430. package/src/shade/renderer/material/chunk_material_draw_side.d.ts +14 -0
  431. package/src/shade/renderer/material/chunk_material_draw_side.d.ts.map +1 -0
  432. package/src/shade/renderer/material/chunk_material_draw_side.js +34 -0
  433. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  434. package/src/shade/renderer/material/standard/fragment_gbuffer.js +8 -1
  435. package/src/shade/renderer/particles/shaders/shader_particle_emit.d.ts.map +1 -1
  436. package/src/shade/renderer/particles/shaders/shader_particle_emit.js +13 -8
  437. package/src/shade/renderer/particles/shaders/shader_particle_simulate.d.ts.map +1 -1
  438. package/src/shade/renderer/particles/shaders/shader_particle_simulate.js +3 -6
  439. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  440. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +13 -1
  441. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +19 -5
  442. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  443. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +23 -5
  444. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.d.ts.map +1 -1
  445. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js +12 -5
  446. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  447. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +4 -12
  448. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts +22 -0
  449. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts.map +1 -0
  450. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.js +36 -0
  451. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts +0 -2
  452. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.d.ts.map +1 -1
  453. package/src/shade/renderer/rasterize/bucket/shader_read_out_bucket.js +4 -5
  454. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +15 -0
  455. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -0
  456. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +35 -0
  457. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.d.ts.map +1 -1
  458. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_mesh_filter_2way.js +5 -3
  459. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.d.ts.map +1 -1
  460. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way.js +11 -4
  461. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.d.ts.map +1 -1
  462. package/src/shade/renderer/rasterize/cull/hzb/shader_hzb_meshlet_filter_1way_full.js +4 -3
  463. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts +10 -1
  464. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
  465. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +38 -29
  466. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts +5 -0
  467. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts.map +1 -1
  468. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.js +63 -55
  469. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
  470. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +3 -0
  471. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.d.ts.map +1 -1
  472. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_count_by_rasterization_bucket.js +94 -90
  473. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.d.ts.map +1 -1
  474. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_extract_bucket.js +13 -5
  475. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
  476. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +96 -89
  477. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
  478. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +3 -1
  479. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.d.ts.map +1 -1
  480. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.js +4 -1
  481. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  482. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +1 -0
  483. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.d.ts.map +1 -1
  484. package/src/shade/renderer/rasterize/expand/mesh/shader_mesh_expand_to_meshlets_prefix.js +100 -89
  485. package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.d.ts.map +1 -1
  486. package/src/shade/renderer/rasterize/expand/mesh/shader_prefix_sum_to_command.js +31 -7
  487. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.d.ts.map +1 -1
  488. package/src/shade/renderer/rasterize/expand/pass0/pass_mesh_cull.js +1 -7
  489. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.d.ts.map +1 -1
  490. package/src/shade/renderer/rasterize/expand/pass1/filter_meshlets.2-way.compute.js +161 -153
  491. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  492. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +2 -7
  493. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  494. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +44 -53
  495. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  496. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -2
  497. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  498. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -2
  499. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -1
  500. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.js +19 -2
  501. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +7 -4
  502. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  503. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -2
  504. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.d.ts.map +1 -1
  505. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.js +15 -4
  506. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  507. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +16 -5
  508. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.d.ts.map +1 -1
  509. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +16 -5
  510. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  511. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +2 -1
  512. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.d.ts.map +1 -1
  513. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.js +4 -0
  514. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  515. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +16 -7
  516. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  517. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +81 -95
  518. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  519. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +26 -39
  520. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  521. package/src/shade/renderer/scene/GPUSceneContext.js +2 -0
  522. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +19 -0
  523. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts.map +1 -1
  524. package/src/shade/renderer/scene/NODE3D_STRUCT.js +34 -0
  525. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts +9 -0
  526. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts.map +1 -0
  527. package/src/shade/renderer/scene/chunk_node3d_flags.js +23 -0
  528. package/src/shade/renderer/scene/chunk_scene_database_access.d.ts.map +1 -1
  529. package/src/shade/renderer/scene/chunk_scene_database_access.js +15 -1
  530. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  531. package/src/shade/renderer/scene/rows/GPUSceneRows.js +9 -1
  532. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts +7 -0
  533. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts.map +1 -1
  534. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js +16 -4
  535. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts +12 -0
  536. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -0
  537. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +18 -0
  538. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts +24 -0
  539. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts.map +1 -0
  540. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.js +33 -0
  541. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts +25 -0
  542. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.d.ts.map +1 -0
  543. package/src/shade/renderer/shadow/map/DEFAULT_SHADOWMAP_UPDATE_BUDGET.js +24 -0
  544. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +10 -0
  545. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  546. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +30 -21
  547. package/src/core/model/node-graph/visual/layout/LayoutBoxGraph.d.ts.map +0 -1
  548. package/src/core/model/node-graph/visual/layout/layout_assign_coordinates.d.ts.map +0 -1
  549. package/src/core/model/node-graph/visual/layout/layout_assign_layers.d.ts.map +0 -1
  550. package/src/core/model/node-graph/visual/layout/layout_break_cycles.d.ts.map +0 -1
  551. package/src/core/model/node-graph/visual/layout/layout_insert_lanes.d.ts.map +0 -1
  552. package/src/core/model/node-graph/visual/layout/layout_order_layers.d.ts.map +0 -1
  553. package/src/core/model/node-graph/visual/layout/layout_orient_links.d.ts.map +0 -1
  554. package/src/shade/playground/vgeo_viewer/README.md +0 -150
  555. package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts +0 -61
  556. package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts.map +0 -1
  557. package/src/shade/playground/vgeo_viewer/cut_geometry.js +0 -460
  558. package/src/shade/playground/vgeo_viewer/index.html +0 -77
  559. package/src/shade/playground/vgeo_viewer/main.d.ts.map +0 -1
  560. package/src/shade/playground/vgeo_viewer/main.js +0 -1343
  561. package/src/shade/playground/vgeo_viewer/sample_asset.d.ts.map +0 -1
  562. package/src/shade/playground/vgeo_viewer/select_cut.d.ts +0 -173
  563. package/src/shade/playground/vgeo_viewer/select_cut.d.ts.map +0 -1
  564. package/src/shade/playground/vgeo_viewer/select_cut.js +0 -481
  565. package/src/shade/renderer/geometry/virtual/build/VGeoFaceGraph.d.ts.map +0 -1
  566. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts +0 -24
  567. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.d.ts.map +0 -1
  568. package/src/shade/renderer/geometry/virtual/build/VGeoGatheredFaces.js +0 -25
  569. package/src/shade/renderer/geometry/virtual/build/VGeoLevel.d.ts.map +0 -1
  570. package/src/shade/renderer/geometry/virtual/build/VGeoLevelOptions.d.ts.map +0 -1
  571. package/src/shade/renderer/geometry/virtual/build/VGeoLevelSummary.d.ts.map +0 -1
  572. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts +0 -20
  573. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.d.ts.map +0 -1
  574. package/src/shade/renderer/geometry/virtual/build/VGeoSubmesh.js +0 -20
  575. package/src/shade/renderer/geometry/virtual/build/VirtualGeometryMesh.d.ts.map +0 -1
  576. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +0 -1
  577. package/src/shade/renderer/geometry/virtual/build/vgeo_bounding_sphere.d.ts.map +0 -1
  578. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts +0 -36
  579. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.d.ts.map +0 -1
  580. package/src/shade/renderer/geometry/virtual/build/vgeo_build_levels.js +0 -1786
  581. package/src/shade/renderer/geometry/virtual/build/vgeo_build_page_nodes.d.ts.map +0 -1
  582. package/src/shade/renderer/geometry/virtual/build/vgeo_build_root_group.d.ts.map +0 -1
  583. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts +0 -29
  584. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.d.ts.map +0 -1
  585. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_normals.js +0 -75
  586. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.d.ts.map +0 -1
  587. package/src/shade/renderer/geometry/virtual/build/vgeo_compute_vertex_tangents.js +0 -143
  588. package/src/shade/renderer/geometry/virtual/build/vgeo_containing_sphere.d.ts.map +0 -1
  589. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts +0 -14
  590. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.d.ts.map +0 -1
  591. package/src/shade/renderer/geometry/virtual/build/vgeo_extract_submesh.js +0 -41
  592. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts +0 -12
  593. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.d.ts.map +0 -1
  594. package/src/shade/renderer/geometry/virtual/build/vgeo_gather_faces.js +0 -71
  595. package/src/shade/renderer/geometry/virtual/build/vgeo_graph_components.d.ts.map +0 -1
  596. package/src/shade/renderer/geometry/virtual/build/vgeo_narrow_indices.d.ts.map +0 -1
  597. package/src/shade/renderer/geometry/virtual/build/vgeo_normalize_source.d.ts.map +0 -1
  598. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +0 -1
  599. package/src/shade/renderer/geometry/virtual/build/vgeo_partition_graph.d.ts.map +0 -1
  600. package/src/shade/renderer/geometry/virtual/build/vgeo_sphere_inflate_to_contain.d.ts.map +0 -1
  601. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set.d.ts.map +0 -1
  602. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.d.ts.map +0 -1
  603. package/src/shade/renderer/geometry/virtual/build/vgeo_split_face_set_packed.js +0 -207
  604. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts +0 -16
  605. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_layers_copy_vertex.d.ts.map +0 -1
  606. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +0 -258
  607. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +0 -1
  608. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +0 -49
  609. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +0 -1
  610. package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts +0 -10
  611. package/src/shade/renderer/loader/gltf/fix_up_material_sides.d.ts.map +0 -1
  612. package/src/shade/renderer/loader/gltf/fix_up_material_sides.js +0 -109
  613. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts +0 -17
  614. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts.map +0 -1
  615. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +0 -78
  616. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts +0 -3
  617. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.d.ts.map +0 -1
  618. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +0 -100
  619. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts +0 -8
  620. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.d.ts.map +0 -1
  621. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.js +0 -26
  622. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts +0 -3
  623. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts.map +0 -1
  624. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_coordinates.d.ts +0 -0
  625. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_assign_layers.d.ts +0 -0
  626. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.d.ts +0 -0
  627. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_break_cycles.js +0 -0
  628. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.d.ts +0 -0
  629. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_insert_lanes.js +0 -0
  630. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.d.ts +0 -0
  631. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_order_layers.js +0 -0
  632. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.d.ts +0 -0
  633. /package/src/core/{model/node-graph/visual/layout → graph/layout/layered}/layout_orient_links.js +0 -0
  634. /package/src/shade/playground/{vgeo_viewer → caldera}/main.d.ts +0 -0
  635. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.d.ts → level/VGeoLevel.d.ts} +0 -0
  636. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.js → level/VGeoLevel.js} +0 -0
  637. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.d.ts → level/VGeoLevelSummary.d.ts} +0 -0
  638. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.js → level/VGeoLevelSummary.js} +0 -0
  639. /package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.d.ts → page/vgeo_page_node_count.d.ts} +0 -0
  640. /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.d.ts → partition/vgeo_graph_components.d.ts} +0 -0
  641. /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.js → partition/vgeo_graph_components.js} +0 -0
  642. /package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.d.ts → partition/vgeo_partition_graph.d.ts} +0 -0
  643. /package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.d.ts → partition/vgeo_split_face_set.d.ts} +0 -0
  644. /package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set_packed.d.ts → partition/vgeo_split_face_set_packed.d.ts} +0 -0
  645. /package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.d.ts → sphere/vgeo_bounding_sphere.d.ts} +0 -0
  646. /package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.d.ts → sphere/vgeo_containing_sphere.d.ts} +0 -0
  647. /package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.d.ts → sphere/vgeo_sphere_inflate_to_contain.d.ts} +0 -0
  648. /package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.d.ts → submesh/vgeo_narrow_indices.d.ts} +0 -0
@@ -0,0 +1,1565 @@
1
+ import { Color } from "../../../core/color/Color.js";
2
+ import Vector3 from "../../../core/geom/Vector3.js";
3
+ import { GIGABYTE } from "../../../core/science/units/memory/GIGABYTE.js";
4
+ import { EntityComponentDataset } from "../../../engine/ecs/EntityComponentDataset.js";
5
+ import { Transform64 } from "../../../engine/ecs/transform/Transform64.js";
6
+ import { OrbitalCameraController } from "../../renderer/camera/OrbitalCameraController.js";
7
+ import { PerspectiveCamera } from "../../renderer/camera/PerspectiveCamera.js";
8
+ import { FramePhase } from "../../renderer/extension/FramePhase.js";
9
+ import { RenderExtension } from "../../renderer/extension/RenderExtension.js";
10
+ import { meshlet_geometry_from_bytes } from "../../renderer/geometry/meshlet_geometry_transfer.js";
11
+ import {
12
+ vgeo_byte_source_over_url
13
+ } from "../../renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js";
14
+ import {
15
+ VGeoContainerReader
16
+ } from "../../renderer/geometry/virtual/format/read/VGeoContainerReader.js";
17
+ import {
18
+ VGeoReadOptions
19
+ } from "../../renderer/geometry/virtual/format/read/VGeoReadOptions.js";
20
+ import { VGEO_CUT_STAT } from "../../renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js";
21
+ import {
22
+ VGeoResidencyOptions
23
+ } from "../../renderer/geometry/virtual/runtime/VGeoResidencyOptions.js";
24
+ import { VirtualGeometry } from "../../renderer/geometry/virtual/runtime/VirtualGeometry.js";
25
+ import { make_sunlight } from "../../renderer/light/make_sunlight.js";
26
+ import { place_light } from "../../renderer/light/place_light.js";
27
+ import { ShadeDrawSide } from "../../renderer/material/ShadeDrawSide.js";
28
+ import { StandardShadeMaterial } from "../../renderer/material/StandardShadeMaterial.js";
29
+ import {
30
+ RasterizationCollectionType
31
+ } from "../../renderer/rasterize/expand/RasterizationCollectionType.js";
32
+ import { Renderer } from "../../renderer/Renderer.js";
33
+ import { place_primitive } from "../../renderer/scene/rows/place_primitive.js";
34
+ import { Scene } from "../../renderer/scene/Scene.js";
35
+ import { load_default_environment } from "../load_default_environment.js";
36
+ import { install_profile_hotkey } from "../profile_hotkey.js";
37
+ import {
38
+ CALDERA_TABLE_ROW,
39
+ caldera_coalesce_ranges,
40
+ caldera_read_geometry_table,
41
+ caldera_read_placements,
42
+ } from "./caldera_format.js";
43
+
44
+ /**
45
+ * Caldera, drawn by meep: 45 districts, millions of placements, and every triangle of it standing
46
+ * where Activision's map put it.
47
+ *
48
+ * [`../vgeo_scene/`](../vgeo_scene/README.md) already puts a converted scene of this order on the
49
+ * screen, and this is deliberately **not** that page. `vgeo_scene` asks one question — what a
50
+ * virtual-geometry cut costs across a whole scene — and answers it with every asset on the same
51
+ * backing, because the point is the cut. This page asks the opposite: what the **ordinary** engine
52
+ * does when a real production map is handed to it whole. So the default backing here is the plain
53
+ * meshlet path every game asset takes, the rows are ordinary mesh rows, and virtual geometry is a
54
+ * *promotion* that a few hundred of the largest geometries get — which is how a game would actually
55
+ * use it, and which makes the two paths comparable inside one frame instead of across two pages.
56
+ *
57
+ * ## What it is looking at
58
+ *
59
+ * Caldera is the Call of Duty: Warzone map, released by Activision as OpenUSD. Its own README calls
60
+ * it 17.5 million primitives and two billion points across two miles square, and it is the largest
61
+ * honest test of a renderer's *scene* — not of one hero asset — that is freely available.
62
+ * `tools/caldera/` converts it; the README beside this file has the numbers that came out.
63
+ *
64
+ * ## What it is measuring
65
+ *
66
+ * Load time first, because that is what the conversion is for. The set holds **serialized
67
+ * `MeshletGeometry`** — {@link MeshletGeometrySerializationAdapter}, the format whose own docblock
68
+ * says building meshlets at load costs 1,246 ms per 199k triangles — so opening a district is a
69
+ * range request, a deserialize and an upload, with no geometry processing anywhere on the path. The
70
+ * panel's `open` row is that claim, per district, in milliseconds.
71
+ *
72
+ * Then everything the scale breaks. Four million mesh rows is two orders of magnitude past what any
73
+ * meep page has asked for, and the things that give way first — a rasterization collection's limit,
74
+ * the geometry manager's arenas, VRAM against half a million BLAS — are exactly what a page like
75
+ * this exists to find. Every one of them is a counter in the panel rather than a stall you have to
76
+ * guess at.
77
+ *
78
+ * ## Running it
79
+ *
80
+ * The dev server in front of the page, and an asset server in front of the converted set — Vite
81
+ * refuses to serve a path outside the workspace, and a converted Caldera has no business inside
82
+ * one:
83
+ *
84
+ * ```
85
+ * npm run dev --workspace @woosh/meep-engine
86
+ * node tools/geometry/virtual/vgeo_serve.js H:/git/caldera/meep
87
+ * ```
88
+ *
89
+ * Then `/src/shade/playground/caldera/index.html`. `?districts=` takes what to open,
90
+ * `?placements=` caps how much of it, and the rest are in the README.
91
+ *
92
+ * @author Alex Goldring
93
+ * @copyright Company Named Limited (c) 2026
94
+ */
95
+
96
+ const element = id => document.getElementById(id);
97
+
98
+ /**
99
+ * Where the asset server is, when the page is not told otherwise. The same default port
100
+ * `vgeo_serve.js` binds, because it is the same server.
101
+ *
102
+ * @type {string}
103
+ */
104
+ const DEFAULT_ORIGIN = 'http://localhost:5200';
105
+
106
+ /**
107
+ * The manifest `caldera_build.js` writes, at the asset origin's root.
108
+ *
109
+ * @type {string}
110
+ */
111
+ const DEFAULT_MANIFEST = 'caldera.meep.json';
112
+
113
+ /**
114
+ * How long a placement pass may hold the thread before it yields.
115
+ *
116
+ * Rows are created one at a time and there are millions of them, so the question is not whether to
117
+ * break the work up but where. A frame's worth is the wrong budget — it makes the page *smooth*
118
+ * while it loads and makes loading take four times as long. Four frames' worth is the compromise:
119
+ * the page still answers a drag, and the scene arrives at something like the rate the machine can
120
+ * actually build it.
121
+ *
122
+ * @type {number}
123
+ */
124
+ const PLACEMENT_BUDGET_MS = 64;
125
+
126
+ /**
127
+ * How long the deserialize loop may hold the thread before it yields.
128
+ *
129
+ * The same reasoning as {@link PLACEMENT_BUDGET_MS} and a different loop, and it was worth splitting
130
+ * because the range was the wrong unit: yielding once per fetched range is fine over a district of
131
+ * four-triangle props and starves the frame completely over the terrain, where one range is one
132
+ * two-million-triangle geometry and four of them are being read at once. The frame counter sitting
133
+ * still for minutes on the `st_*` districts is what this is for.
134
+ *
135
+ * @type {number}
136
+ */
137
+ const DESERIALIZE_BUDGET_MS = 32;
138
+
139
+ /**
140
+ * `.vgeo` containers opened at once. The same figure `vgeo_scene` settled on: enough that the
141
+ * round trips overlap, few enough that a browser does not queue them behind each other anyway.
142
+ *
143
+ * @type {number}
144
+ */
145
+ const OPEN_CONCURRENCY = 24;
146
+
147
+ /**
148
+ * The row count past which this set has not been held to be safe.
149
+ *
150
+ * It used to be 2²⁰, where the picture stopped being correct — and that was never a row count at
151
+ * all: the dispatch that broke was over *meshlets*, and 2²⁰ rows is where this scene's meshlet
152
+ * count crossed it. Fixed in `962e79183` / `53deaa9b5`, so a scene of any row count draws what it
153
+ * is given now.
154
+ *
155
+ * What is left is memory, which is the wall the README calls the second one: this set ran out of
156
+ * VRAM on a 24 GB card somewhere between 2.2 and 3.7 million rows, once saying so out of `render`
157
+ * and once by thrashing until the main thread stopped answering. 2.2 million is where it said so,
158
+ * so it is the last figure this page can stand behind.
159
+ *
160
+ * The page does not stop here, because stopping would hide it. It says so in the panel instead.
161
+ *
162
+ * @type {number}
163
+ */
164
+ const ROWS_LAST_KNOWN_GOOD = 2_200_000;
165
+
166
+ /**
167
+ * What a container's always-resident set costs at the median across a converted set — header, level
168
+ * table, root frame. Asking for this much on the first read opens the container in **one** round
169
+ * trip instead of the reader's default 64 KiB of over-fetch.
170
+ *
171
+ * @type {number}
172
+ */
173
+ const HEAD_PREFIX_BYTES = 8 * 1024;
174
+
175
+ /**
176
+ * @type {Record<string, ShadeDrawSide>}
177
+ */
178
+ const DRAW_SIDES = Object.freeze({
179
+ front: ShadeDrawSide.Front,
180
+ back: ShadeDrawSide.Back,
181
+ double: ShadeDrawSide.Double,
182
+ });
183
+
184
+
185
+ /**
186
+ * Opens the VG frame before anything records a rasterization pass, because the seam inside those
187
+ * passes writes into the feedback buffer this creates.
188
+ */
189
+ class VGeoFrameStart extends RenderExtension {
190
+ name = "caldera/vgeo frame start";
191
+
192
+ phase = FramePhase.FrameStart;
193
+
194
+ /**
195
+ * @param {CalderaPlayground} page
196
+ */
197
+ constructor(page) {
198
+ super();
199
+
200
+ this.page = page;
201
+ }
202
+
203
+ record(frame) {
204
+ this.page.runtime.begin_frame({ graph: frame.graph });
205
+ }
206
+ }
207
+
208
+
209
+ /**
210
+ * Schedules the readback and closes the frame, after everything that could have cut has cut.
211
+ */
212
+ class VGeoFrameEnd extends RenderExtension {
213
+ name = "caldera/vgeo frame end";
214
+
215
+ phase = FramePhase.BeforePresent;
216
+
217
+ /**
218
+ * @param {CalderaPlayground} page
219
+ */
220
+ constructor(page) {
221
+ super();
222
+
223
+ this.page = page;
224
+ }
225
+
226
+ record(frame) {
227
+ this.page.feedback.read(frame.graph);
228
+ this.page.runtime.end_frame();
229
+ }
230
+ }
231
+
232
+
233
+ class CalderaPlayground {
234
+
235
+ /** @type {Renderer} */
236
+ renderer;
237
+
238
+ /** @type {HTMLCanvasElement} */
239
+ canvas;
240
+
241
+ /** @type {Scene} */
242
+ scene;
243
+
244
+ /** @type {GPUSceneContext} */
245
+ scene_context;
246
+
247
+ /** @type {PerspectiveCamera} */
248
+ camera;
249
+
250
+ /** @type {OrbitalCameraController} */
251
+ controller;
252
+
253
+ /**
254
+ * The one material the whole map is drawn with.
255
+ *
256
+ * Caldera ships without materials or textures — its own README says so, and says why: the set
257
+ * is about the relationships between spatial elements rather than about how they look. So there
258
+ * is nothing to shade this with but geometry, and one material is the honest representation of
259
+ * that rather than a simplification this page is making.
260
+ *
261
+ * @type {StandardShadeMaterial}
262
+ */
263
+ material;
264
+
265
+ /** @type {object|undefined} */
266
+ manifest;
267
+
268
+ /**
269
+ * The geometry table, as column arrays.
270
+ * @type {ReturnType<typeof caldera_read_geometry_table>|undefined}
271
+ */
272
+ table;
273
+
274
+ /** @type {string} where the manifest came from; every other URL resolves against it */
275
+ base = '';
276
+
277
+ /**
278
+ * Meshlet geometries that have been deserialized, by geometry id.
279
+ *
280
+ * Held because a geometry is shared between districts — that is the whole reason the set has a
281
+ * geometry table rather than a scene per district — and because dropping one would drop the
282
+ * rows that draw it.
283
+ *
284
+ * @type {Map<number, MeshletGeometry>}
285
+ */
286
+ geometries = new Map();
287
+
288
+ /**
289
+ * Geometries placed on the virtual backing instead, by geometry id, with the reader kept beside
290
+ * each so nothing collects it out from under the residency.
291
+ *
292
+ * @type {Map<number, {geometry: VirtualGeometry, reader: VGeoContainerReader}>}
293
+ */
294
+ virtual = new Map();
295
+
296
+ /**
297
+ * The merged assets — one container each, holding what the source had cut into pieces.
298
+ *
299
+ * Caldera's ground arrives this way: 728 geometries placed once each on an 832 m grid, baked by
300
+ * `caldera_build.js --merge` into a single `.vgeo`. They are held for the same reason the other
301
+ * readers are, and they are placed at the identity because the baking already put every vertex
302
+ * in world space.
303
+ *
304
+ * @type {Array<{record: object, geometry: VirtualGeometry, reader: VGeoContainerReader}>}
305
+ */
306
+ merged = [];
307
+
308
+ /** @type {number} placements turned into rows */
309
+ placed = 0;
310
+
311
+ /** @type {number} how many of those were drawn from the virtual backing */
312
+ placed_virtual = 0;
313
+
314
+ /** @type {number} instanced triangles standing in the scene */
315
+ instanced_triangles = 0;
316
+
317
+ /** @type {number} distinct source triangles behind them */
318
+ source_triangles = 0;
319
+
320
+ /** @type {number} bytes of pack, table, placements and container heads fetched */
321
+ bytes_fetched = 0;
322
+
323
+ /** @type {number} geometries the set declared but this page could not open */
324
+ refused = 0;
325
+
326
+ /** @type {number} milliseconds from the first district's first byte to its last row */
327
+ open_ms = 0;
328
+
329
+ /** @type {number} */
330
+ frames = 0;
331
+
332
+ /** @type {number} */
333
+ frame_ms = 0;
334
+
335
+ /** @type {number} what the last frame cost on the CPU, which at this scale is the interesting half */
336
+ cpu_ms = 0;
337
+
338
+ /** @type {string} */
339
+ status_text = '';
340
+
341
+ /** @type {number} */
342
+ screen_width = 1;
343
+
344
+ /** @type {number} */
345
+ screen_height = 1;
346
+
347
+ /**
348
+ * Districts as the manifest declares them, with what this page has done about each.
349
+ * @type {Array<object & {state: string}>}
350
+ */
351
+ districts = [];
352
+
353
+ /**
354
+ * Whether the promoted geometries are drawn from their `.vgeo` rather than their meshlet blob.
355
+ * @type {boolean}
356
+ */
357
+ virtual_enabled = false;
358
+
359
+ /** @type {GPUVirtualGeometryRuntime|undefined} */
360
+ runtime;
361
+
362
+ /** @type {VGeoResidency|undefined} */
363
+ residency;
364
+
365
+ /** @type {GPUVirtualGeometryFeedback|undefined} */
366
+ feedback;
367
+
368
+ /** @type {number} half the diagonal of the whole map, which sets the far plane */
369
+ world_radius = 1000;
370
+
371
+ /**
372
+ * What the virtual-geometry tables were sized to, kept so a session can read back the numbers
373
+ * the page chose rather than infer them.
374
+ *
375
+ * @type {object|undefined}
376
+ */
377
+ capacities;
378
+
379
+ /**
380
+ * What killed the frame loop, if anything did.
381
+ *
382
+ * A scene this size runs out of somewhere — VRAM first — and the engine says so, out of
383
+ * `render`: *Failed to allocate GPU memory for meshlet batch*. The loop catches that and stops,
384
+ * and without this flag the loader carries on behind it for another twenty minutes, fetching
385
+ * geometry for a renderer that is never going to draw again and burying the one message that
386
+ * explained what happened. Whatever is on the screen when this is set is what the machine could
387
+ * hold, which is the reading the page exists to take.
388
+ *
389
+ * @type {Error|undefined}
390
+ */
391
+ fatal;
392
+
393
+ /**
394
+ * Put the camera at `eye` looking at `target`, and rescale everything that is a function of how
395
+ * far away it is.
396
+ *
397
+ * The near and far planes are the part that matters here: Caldera is three kilometres across and
398
+ * a viewer stands centimetres from a wall inside it, so a fixed pair of planes either z-fights
399
+ * everything close or clips everything far. Deriving them from the distance is what lets one
400
+ * page do both.
401
+ *
402
+ * @param {ArrayLike<number>} eye
403
+ * @param {ArrayLike<number>} target
404
+ */
405
+ look(eye, target) {
406
+ const from = new Vector3(eye[0], eye[1], eye[2]);
407
+ const to = new Vector3(target[0], target[1], target[2]);
408
+
409
+ const distance = Math.max(Vector3.distance(from, to), 1e-3);
410
+
411
+ this.camera.near = distance * 0.002;
412
+ this.camera.far = Math.max(distance * 2000, this.world_radius * 4);
413
+
414
+ this.controller.distanceLimits.min = distance * 0.01;
415
+ this.controller.distanceLimits.max = Math.max(distance * 2000, this.world_radius * 4);
416
+ this.controller.movement_speed_scale = distance;
417
+
418
+ this.controller.look(from, to);
419
+
420
+ // the pixels the camera is about to cover have no correspondence in a TAA history written
421
+ // from where it was standing
422
+ this.renderer.indicate_view_change();
423
+ }
424
+
425
+ /**
426
+ * @param {string} text
427
+ * @param {boolean} [error]
428
+ */
429
+ status(text, error = false) {
430
+ this.status_text = text;
431
+
432
+ const node = element('status');
433
+
434
+ node.textContent = text;
435
+ node.classList.toggle('error', error);
436
+ }
437
+
438
+ /**
439
+ * Size the canvas to the document.
440
+ *
441
+ * The **document**, not the canvas: `Renderer` writes an inline `style.width` and `style.height`
442
+ * every time it configures the context, so measuring the canvas measures what the renderer last
443
+ * decided rather than what the window now is, and the two ratchet against each other.
444
+ */
445
+ fit() {
446
+ const renderer = this.renderer;
447
+
448
+ const viewport = document.documentElement;
449
+
450
+ renderer.pixel_ratio = globalThis.devicePixelRatio || 1;
451
+
452
+ renderer.resize(
453
+ Math.max(1, Math.floor(viewport.clientWidth)),
454
+ Math.max(1, Math.floor(viewport.clientHeight))
455
+ );
456
+
457
+ this.camera.aspect = renderer.aspect_ratio;
458
+
459
+ const resolution = renderer.output_resolution;
460
+ const scale = renderer.internal_resolution_scale;
461
+
462
+ this.screen_width = Math.max(1, Math.floor(resolution.x * scale));
463
+ this.screen_height = Math.max(1, Math.floor(resolution.y * scale));
464
+ }
465
+
466
+ /**
467
+ * @param {string} path relative to the manifest
468
+ * @param {{start: number, end: number}} [range] inclusive-exclusive, as the table reports one
469
+ * @returns {Promise<ArrayBuffer>}
470
+ */
471
+ async fetch_bytes(path, range) {
472
+ const url = new URL(path, this.base).href;
473
+
474
+ const options = range === undefined
475
+ ? undefined
476
+ : { headers: { Range: `bytes=${range.start}-${range.end - 1}` } };
477
+
478
+ const response = await fetch(url, options);
479
+
480
+ if (!response.ok) {
481
+ throw new Error(`${path} answered HTTP ${response.status}`);
482
+ }
483
+
484
+ /*
485
+ A `200` where a range was asked for is a server that ignored the request and sent the head
486
+ of the file — plausible bytes at the wrong offset, which downstream is a meshlet geometry
487
+ built out of another geometry's middle. The same check `vgeo_fetch_byte_source` makes, for
488
+ the same reason, and it is the one thing a static server in front of this set has to get
489
+ right.
490
+ */
491
+ if (range !== undefined && response.status !== 206) {
492
+ throw new Error(
493
+ `${path} answered HTTP ${response.status} to a Range request — the asset server`
494
+ + ` must support ranges; vgeo_serve.js does`
495
+ );
496
+ }
497
+
498
+ const bytes = await response.arrayBuffer();
499
+
500
+ this.bytes_fetched += bytes.byteLength;
501
+
502
+ return bytes;
503
+ }
504
+
505
+ /**
506
+ * Open every district that was asked for, one after another, placing each as it lands.
507
+ *
508
+ * Sequential rather than concurrent, and that is the shape of the thing rather than a
509
+ * simplification: placing rows is main-thread work that does not overlap with itself, so
510
+ * fetching a second district while the first is still being placed buys nothing but memory. The
511
+ * fetch inside a district is concurrent, which is where the overlap actually is.
512
+ *
513
+ * @param {number} placement_budget stop once this many rows exist
514
+ * @returns {Promise<void>}
515
+ */
516
+ async open_districts(placement_budget) {
517
+ const started = performance.now();
518
+
519
+ /*
520
+ The merged assets first, and not because they are large — because they are the *ground*. A
521
+ scene that fills in district by district over four minutes reads as a map being built if
522
+ the terrain is under it from the first second, and as debris floating in the sea if it is
523
+ not.
524
+ */
525
+ await this.open_merged();
526
+
527
+ for (const district of this.districts) {
528
+ if (this.fatal !== undefined || this.placed >= placement_budget) {
529
+ district.state = 'skipped';
530
+
531
+ continue;
532
+ }
533
+
534
+ district.state = 'loading';
535
+
536
+ this.paint_districts();
537
+
538
+ try {
539
+ await this.open_district(district, placement_budget);
540
+
541
+ district.state = 'done';
542
+ } catch (error) {
543
+ district.state = 'failed';
544
+
545
+ console.warn(`${district.name} — ${error.message ?? error}`);
546
+ }
547
+
548
+ this.paint_districts();
549
+
550
+ this.status(
551
+ `${this.placed.toLocaleString()} placements`
552
+ + `, ${(this.geometries.size + this.virtual.size).toLocaleString()} geometries`
553
+ + `, ${((performance.now() - started) / 1000).toFixed(1)} s`
554
+ );
555
+ }
556
+
557
+ this.open_ms = performance.now() - started;
558
+ }
559
+
560
+ /**
561
+ * Open every merged asset the manifest names and place each at the identity.
562
+ *
563
+ * One row apiece, and that is the point of them: the ground is 24 million triangles that would
564
+ * otherwise be 728 containers and 728 rows, each with its own cluster DAG over a surface it
565
+ * shares with its neighbours. As one asset the cut sees one surface, and a coarse level may
566
+ * simplify across a seam that is no longer there.
567
+ *
568
+ * These have no meshlet alternative — the pieces they were baked from have no blobs of their
569
+ * own — so `?vgeo=off` does not apply to them, and a page that could not open one is missing
570
+ * its terrain rather than drawing it the other way.
571
+ *
572
+ * @returns {Promise<void>}
573
+ */
574
+ async open_merged() {
575
+ const records = this.manifest.merged ?? [];
576
+
577
+ if (records.length === 0 || this.runtime === undefined) {
578
+ return;
579
+ }
580
+
581
+ const options = new VGeoReadOptions();
582
+
583
+ options.verify_frames = true;
584
+ options.track_selectability = false;
585
+ options.head_prefix_bytes = HEAD_PREFIX_BYTES;
586
+
587
+ const transform = new Transform64();
588
+
589
+ for (const record of records) {
590
+ try {
591
+ const source = vgeo_byte_source_over_url(
592
+ new URL(record.file, this.base).href, record.bytes
593
+ );
594
+
595
+ const reader = await VGeoContainerReader.open(source, options);
596
+
597
+ const geometry = new VirtualGeometry(reader);
598
+
599
+ geometry.vg_row = this.residency.register(reader);
600
+
601
+ place_primitive(this.scene_context, geometry, this.material, transform);
602
+
603
+ this.merged.push({ record, geometry, reader });
604
+
605
+ this.placed++;
606
+ this.placed_virtual++;
607
+ this.source_triangles += record.triangles;
608
+ this.instanced_triangles += record.triangles;
609
+ } catch (error) {
610
+ this.refused++;
611
+
612
+ console.warn(`${record.file} — ${error.message ?? error}`);
613
+ }
614
+ }
615
+
616
+ this.status(
617
+ `${this.merged.length} merged asset${this.merged.length === 1 ? '' : 's'}`
618
+ + `, ${this.merged.reduce((t, m) => t + m.record.triangles, 0).toLocaleString()} triangles`
619
+ );
620
+ }
621
+
622
+ /**
623
+ * @param {object} district
624
+ * @param {number} placement_budget
625
+ * @returns {Promise<void>}
626
+ */
627
+ async open_district(district, placement_budget) {
628
+ const placements = caldera_read_placements(await this.fetch_bytes(district.file));
629
+
630
+ /*
631
+ What this district needs that is not already here. A geometry shared with a district that
632
+ opened earlier costs nothing the second time, which is most of why the whole map fits at
633
+ all: the set's 3.8 million placements stand on a far smaller number of geometries, and the
634
+ sharing is between districts as much as inside them.
635
+ */
636
+ const wanted_meshlets = [];
637
+ const wanted_virtual = [];
638
+
639
+ const seen = new Set();
640
+
641
+ for (let i = 0; i < placements.count; i++) {
642
+ const id = placements.geometry[i];
643
+
644
+ if (seen.has(id) || this.geometries.has(id) || this.virtual.has(id)) {
645
+ continue;
646
+ }
647
+
648
+ seen.add(id);
649
+
650
+ if (this.use_virtual(id)) {
651
+ wanted_virtual.push(id);
652
+ } else {
653
+ wanted_meshlets.push(id);
654
+ }
655
+ }
656
+
657
+ await Promise.all([
658
+ this.open_virtual(wanted_virtual),
659
+ this.open_meshlets(wanted_meshlets),
660
+ ]);
661
+
662
+ await this.place(placements, placement_budget);
663
+ }
664
+
665
+ /**
666
+ * Whether this geometry is drawn from a `.vgeo` rather than from its meshlet blob.
667
+ *
668
+ * Both were built for it — `caldera_build.js` promotes by triangle count and keeps the meshlet
669
+ * blob anyway — so this is a runtime choice and `?vgeo=off` unmakes it without rebuilding
670
+ * anything. What it costs is not symmetric: the meshlet blob of a 200k-triangle geometry is
671
+ * fetched and uploaded whole, and the `.vgeo` of one is a 2.5 KiB head and whatever the cut
672
+ * asks for afterwards.
673
+ *
674
+ * @param {number} id
675
+ * @returns {boolean}
676
+ */
677
+ use_virtual(id) {
678
+ return this.virtual_enabled && this.table.vgeo[id] !== CALDERA_TABLE_ROW.NO_VGEO;
679
+ }
680
+
681
+ /**
682
+ * Fetch and deserialize a set of meshlet geometries.
683
+ *
684
+ * The fetch is by **coalesced range**, which is the one thing about this path that had to be
685
+ * designed rather than written: a district wants tens of thousands of blobs, a request each is
686
+ * tens of thousands of round trips, and the blobs are laid down in geometry-id order precisely
687
+ * so that a district's own geometry is a handful of long runs. What comes back is sliced apart
688
+ * here.
689
+ *
690
+ * @param {number[]} ids
691
+ * @returns {Promise<void>}
692
+ */
693
+ async open_meshlets(ids) {
694
+ if (ids.length === 0) {
695
+ return;
696
+ }
697
+
698
+ const table = this.table;
699
+ const packs = this.manifest.packs;
700
+
701
+ const ranges = caldera_coalesce_ranges(ids, table);
702
+
703
+ let next = 0;
704
+
705
+ const worker = async () => {
706
+ while (next < ranges.length && this.fatal === undefined) {
707
+ const range = ranges[next++];
708
+
709
+ const bytes = await this.fetch_bytes(packs[range.pack].file, range);
710
+
711
+ let deadline = performance.now() + DESERIALIZE_BUDGET_MS;
712
+
713
+ for (const id of range.ids) {
714
+ const at = table.offset[id] - range.start;
715
+
716
+ try {
717
+ const geometry = meshlet_geometry_from_bytes(
718
+ bytes.slice(at, at + table.bytes[id])
719
+ );
720
+
721
+ this.geometries.set(id, geometry);
722
+
723
+ this.source_triangles += table.triangles[id];
724
+ } catch (error) {
725
+ this.refused++;
726
+
727
+ console.warn(`geometry ${id} — ${error.message ?? error}`);
728
+ }
729
+
730
+ if (performance.now() >= deadline) {
731
+ await new Promise(resolve => setTimeout(resolve, 0));
732
+
733
+ deadline = performance.now() + DESERIALIZE_BUDGET_MS;
734
+ }
735
+ }
736
+
737
+ // a pack range is tens of megabytes and the geometries out of it are already
738
+ // copies; yielding here is what lets the collector have it back
739
+ await new Promise(resolve => setTimeout(resolve, 0));
740
+ }
741
+ };
742
+
743
+ const workers = [];
744
+
745
+ for (let i = 0; i < Math.min(4, ranges.length); i++) {
746
+ workers.push(worker());
747
+ }
748
+
749
+ await Promise.all(workers);
750
+ }
751
+
752
+ /**
753
+ * Open the `.vgeo` containers for a set of geometries and register each with the one residency.
754
+ *
755
+ * @param {number[]} ids
756
+ * @returns {Promise<void>}
757
+ */
758
+ async open_virtual(ids) {
759
+ if (ids.length === 0) {
760
+ return;
761
+ }
762
+
763
+ const options = new VGeoReadOptions();
764
+
765
+ /*
766
+ Frame verification on. These are bytes off a network rather than bytes a tool just wrote,
767
+ which is the case the check exists for and the reason §10 leaves it off by default.
768
+ */
769
+ options.verify_frames = true;
770
+
771
+ // the runtime answers activation out of its arena; the reader's own counters would be
772
+ // megabytes at this instance count and are read by nothing
773
+ options.track_selectability = false;
774
+
775
+ options.head_prefix_bytes = HEAD_PREFIX_BYTES;
776
+
777
+ let next = 0;
778
+
779
+ const worker = async () => {
780
+ while (next < ids.length && this.fatal === undefined) {
781
+ const id = ids[next++];
782
+
783
+ const record = this.manifest.vgeo[this.table.vgeo[id]];
784
+
785
+ try {
786
+ const source = vgeo_byte_source_over_url(
787
+ new URL(record.file, this.base).href, record.bytes
788
+ );
789
+
790
+ const reader = await VGeoContainerReader.open(source, options);
791
+
792
+ const geometry = new VirtualGeometry(reader);
793
+
794
+ geometry.vg_row = this.residency.register(reader);
795
+
796
+ this.virtual.set(id, { geometry, reader });
797
+
798
+ this.source_triangles += this.table.triangles[id];
799
+ } catch (error) {
800
+ this.refused++;
801
+
802
+ console.warn(`${record.file} — ${error.message ?? error}`);
803
+ }
804
+ }
805
+ };
806
+
807
+ const workers = [];
808
+
809
+ for (let i = 0; i < Math.min(OPEN_CONCURRENCY, ids.length); i++) {
810
+ workers.push(worker());
811
+ }
812
+
813
+ await Promise.all(workers);
814
+ }
815
+
816
+ /**
817
+ * Turn placements into rows, yielding often enough that the page stays alive.
818
+ *
819
+ * A row is an entity, three components and a mesh row, and there are millions of them; this is
820
+ * the loop that decides whether the page loads in a minute or in five, and the only knob it has
821
+ * is how long it holds the thread between yields. It is also the loop that will break first
822
+ * when this set is scaled up, which is the point of the page.
823
+ *
824
+ * @param {{count: number, geometry: Uint32Array, matrices: Float32Array}} placements
825
+ * @param {number} budget stop once this many rows exist
826
+ * @returns {Promise<void>}
827
+ */
828
+ async place(placements, budget) {
829
+ const transform = new Transform64();
830
+
831
+ const table = this.table;
832
+
833
+ let deadline = performance.now() + PLACEMENT_BUDGET_MS;
834
+
835
+ for (let i = 0; i < placements.count; i++) {
836
+ if (this.fatal !== undefined || this.placed >= budget) {
837
+ return;
838
+ }
839
+
840
+ const id = placements.geometry[i];
841
+
842
+ const virtual = this.virtual.get(id);
843
+ const geometry = virtual?.geometry ?? this.geometries.get(id);
844
+
845
+ if (geometry === undefined) {
846
+ // a geometry that refused; its placements are holes rather than a failure
847
+ continue;
848
+ }
849
+
850
+ /*
851
+ The source matrix, decomposed. A transform row carries a TRS and the GPU composes the
852
+ world from it, so an instance carrying **shear** cannot be represented exactly and is
853
+ placed at the closest rigid-plus-scale fit. The engine reverses a reflected instance's
854
+ winding in the vertex shader off the row's own flag, so a negative determinant needs
855
+ nothing done to it here for the picture to be right.
856
+ */
857
+ transform.fromMatrix(placements.matrices.subarray(i * 16, i * 16 + 16));
858
+
859
+ place_primitive(this.scene_context, geometry, this.material, transform);
860
+
861
+ this.placed++;
862
+
863
+ if (virtual !== undefined) {
864
+ this.placed_virtual++;
865
+ }
866
+
867
+ this.instanced_triangles += table.triangles[id];
868
+
869
+ if (performance.now() >= deadline) {
870
+ await new Promise(resolve => setTimeout(resolve, 0));
871
+
872
+ deadline = performance.now() + PLACEMENT_BUDGET_MS;
873
+ }
874
+ }
875
+ }
876
+
877
+ paint_districts() {
878
+ const done = this.districts.reduce(
879
+ (total, district) => total + (district.state === 'done' ? district.placements : 0), 0
880
+ );
881
+
882
+ const declared = this.districts.reduce(
883
+ (total, district) => total + district.placements, 0
884
+ );
885
+
886
+ element('progress').firstElementChild.style.width =
887
+ `${declared === 0 ? 0 : Math.round(100 * done / declared)}%`;
888
+
889
+ element('districts').innerHTML = this.districts.map(district => (
890
+ `<div class="row ${district.state}"><span>${escape_html(district.name)}</span>`
891
+ + `<span>${district.state === 'done'
892
+ ? district.placements.toLocaleString()
893
+ : district.state}</span></div>`
894
+ )).join('');
895
+ }
896
+
897
+ /**
898
+ * Everything the panel shows and a profile capture records, in one object.
899
+ *
900
+ * @returns {object}
901
+ */
902
+ state() {
903
+ const stats = this.feedback?.stats;
904
+ const pool = this.runtime?.pool;
905
+
906
+ const limits = this.renderer.graphics.collection_limits;
907
+
908
+ return {
909
+ frames: this.frames,
910
+ frame_ms: this.frame_ms,
911
+ cpu_ms: this.cpu_ms,
912
+ status: this.status_text,
913
+ fatal: this.fatal === undefined ? null : (this.fatal.message ?? String(this.fatal)),
914
+ screen_width: this.screen_width,
915
+ screen_height: this.screen_height,
916
+ scene: {
917
+ source: this.manifest?.source ?? '',
918
+ districts: this.districts.filter(d => d.state === 'done').length,
919
+ districts_declared: this.districts.length,
920
+ placements: this.placed,
921
+ placements_virtual: this.placed_virtual,
922
+ geometries: this.geometries.size,
923
+ geometries_virtual: this.virtual.size + this.merged.length,
924
+ merged: this.merged.length,
925
+ merged_triangles: this.merged.reduce((t, m) => t + m.record.triangles, 0),
926
+ source_triangles: this.source_triangles,
927
+ instanced_triangles: this.instanced_triangles,
928
+ bytes_fetched: this.bytes_fetched,
929
+ refused: this.refused,
930
+ open_ms: this.open_ms,
931
+ },
932
+ /*
933
+ The **limits**, not the counts: what the rasterization collections have had to grow to
934
+ in order to hold what the frame handed them. At this scale that is the number worth
935
+ watching — a collection that stops growing while placements keep arriving is one
936
+ dropping the difference, and `dropped` beside it is where that shows up.
937
+ */
938
+ collections: {
939
+ meshes: limits.get_limit(RasterizationCollectionType.Meshes),
940
+ meshlets: limits.get_limit(RasterizationCollectionType.Meshlets),
941
+ },
942
+ capacities: this.capacities,
943
+ cut: stats === undefined ? undefined : {
944
+ tau: this.runtime.tau,
945
+ // the runtime's own, not what the page asked for — it is the one that was used
946
+ queue_capacity: this.runtime.queue_capacity,
947
+ drawn: stats[VGEO_CUT_STAT.DRAWN],
948
+ clamped: stats[VGEO_CUT_STAT.CLAMPED],
949
+ pages: stats[VGEO_CUT_STAT.PAGES],
950
+ clusters_tested: stats[VGEO_CUT_STAT.CLUSTERS_TESTED],
951
+ meshlets_dropped: stats[VGEO_CUT_STAT.MESHLETS_DROPPED],
952
+ instances_dropped: stats[VGEO_CUT_STAT.INSTANCES_DROPPED],
953
+ queue_dropped: stats[VGEO_CUT_STAT.QUEUE_DROPPED],
954
+ queue_remaining: stats[VGEO_CUT_STAT.QUEUE_REMAINING],
955
+ max_iterations: this.runtime.max_iterations,
956
+ },
957
+ vram: pool === undefined ? undefined : {
958
+ pages: pool.resident_page_count,
959
+ bytes: pool.resident_bytes,
960
+ capacity: pool.tables.page_capacity,
961
+ evicted: this.residency.evicted_vram_count,
962
+ refused: this.residency.refused_install_count,
963
+ },
964
+ camera: {
965
+ eye: Array.from(this.camera.transform.translation),
966
+ target: this.controller.target.toJSON(),
967
+ distance: this.controller.distance,
968
+ },
969
+ };
970
+ }
971
+ }
972
+
973
+
974
+ function human_bytes(bytes) {
975
+ if (bytes < 1024) {
976
+ return `${bytes} B`;
977
+ }
978
+
979
+ if (bytes < 1024 * 1024) {
980
+ return `${(bytes / 1024).toFixed(1)} KiB`;
981
+ }
982
+
983
+ if (bytes < 1024 * 1024 * 1024) {
984
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
985
+ }
986
+
987
+ return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
988
+ }
989
+
990
+
991
+ function escape_html(text) {
992
+ return String(text).replace(/[&<>"]/g, c => (
993
+ { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]
994
+ ));
995
+ }
996
+
997
+
998
+ /**
999
+ * How large the virtual-geometry tables have to be for the containers this set holds.
1000
+ *
1001
+ * These are the capacities a *scene* reaches first, and two of them degrade silently — a lookup
1002
+ * arena too small refuses containers, and a claim arena too small drops instances. Both are
1003
+ * functions of the set rather than of the frame, so they come off the manifest rather than out of a
1004
+ * constant.
1005
+ *
1006
+ * @param {object} manifest
1007
+ * @param {number} page_capacity
1008
+ * @returns {{geometry_capacity: number, lookup_words: number, claim_word_capacity: number}}
1009
+ */
1010
+ function virtual_capacities(manifest, page_capacity, requested_queue) {
1011
+ const merged = manifest.merged ?? [];
1012
+
1013
+ /*
1014
+ Both kinds, because the tables are per *container* and the residency does not care which list a
1015
+ container came from. A merged asset is one container and a large share of the pages — Caldera's
1016
+ ground is one of 228 and a third of them.
1017
+ */
1018
+ const containers = Math.max(1, (manifest.vgeo?.length ?? 0) + merged.length);
1019
+
1020
+ const pages = Math.max(
1021
+ 1, (manifest.vgeo_pages ?? 0) + merged.reduce((total, m) => total + (m.pages ?? 0), 0)
1022
+ );
1023
+
1024
+ /*
1025
+ Every instance of every promoted container, plus one apiece for the merged assets. The builder
1026
+ counts them while it writes the district files, because the count is a property of the
1027
+ placements and nothing else on either side of the conversion knows it.
1028
+ */
1029
+ const instances = (manifest.vgeo ?? []).reduce((total, e) => total + (e.instances ?? 0), 0)
1030
+ + merged.length;
1031
+
1032
+ const queue = requested_queue > 0
1033
+ ? requested_queue
1034
+ : Math.min(
1035
+ 1 << 22,
1036
+ 1 << Math.ceil(Math.log2(Math.max(1 << 18, Math.max(1, instances) * 16)))
1037
+ );
1038
+
1039
+ return {
1040
+ geometry_capacity: containers + 64,
1041
+ lookup_words: 1 << Math.ceil(Math.log2(Math.max(1 << 16, pages * 1.5))),
1042
+ claim_word_capacity: Math.ceil(
1043
+ Math.max(1, manifest.max_vgeo_instances ?? 1) * page_capacity / 32
1044
+ ) * 2,
1045
+ queue_capacity: queue,
1046
+ instances,
1047
+ };
1048
+ }
1049
+
1050
+
1051
+ /**
1052
+ * Where the converted set is.
1053
+ *
1054
+ * `?scene=` names a manifest outright; otherwise the asset server's root is asked, which is the
1055
+ * common case and saves typing a URL the server already knows.
1056
+ *
1057
+ * @returns {Promise<string>}
1058
+ */
1059
+ async function resolve_manifest_url() {
1060
+ const parameters = new URL(globalThis.location.href).searchParams;
1061
+
1062
+ const named = parameters.get('scene');
1063
+
1064
+ if (named !== null) {
1065
+ return new URL(named, globalThis.location.href).href;
1066
+ }
1067
+
1068
+ const origin = parameters.get('origin') ?? DEFAULT_ORIGIN;
1069
+
1070
+ return new URL(DEFAULT_MANIFEST, origin.endsWith('/') ? origin : `${origin}/`).href;
1071
+ }
1072
+
1073
+
1074
+ async function start() {
1075
+ const canvas = element('view');
1076
+
1077
+ const page = new CalderaPlayground();
1078
+
1079
+ page.canvas = canvas;
1080
+
1081
+ const parameters = new URL(globalThis.location.href).searchParams;
1082
+
1083
+ page.status('reading the manifest…');
1084
+
1085
+ const manifest_url = await resolve_manifest_url();
1086
+
1087
+ const response = await fetch(manifest_url);
1088
+
1089
+ if (!response.ok) {
1090
+ throw new Error(
1091
+ `${manifest_url} answered HTTP ${response.status}`
1092
+ + ` — is \`node tools/geometry/virtual/vgeo_serve.js <converted set>\` running?`
1093
+ );
1094
+ }
1095
+
1096
+ const manifest = await response.json();
1097
+
1098
+ if (manifest.format !== 1) {
1099
+ throw new Error(
1100
+ `${manifest_url} is a format-${manifest.format} set and this page reads format 1`
1101
+ );
1102
+ }
1103
+
1104
+ page.manifest = manifest;
1105
+ page.base = manifest_url;
1106
+
1107
+ page.status('reading the geometry table…');
1108
+
1109
+ page.table = caldera_read_geometry_table(
1110
+ await page.fetch_bytes(manifest.geometry_table)
1111
+ );
1112
+
1113
+ if (page.table.count !== manifest.geometry_count) {
1114
+ throw new Error(
1115
+ `the manifest names ${manifest.geometry_count} geometries and the table holds`
1116
+ + ` ${page.table.count}`
1117
+ );
1118
+ }
1119
+
1120
+ /*
1121
+ Which districts, and how much of them. Both default to everything, because the whole point of
1122
+ the page is the whole map — but a machine that cannot hold it should find out by watching a
1123
+ counter stop rather than by waiting for a tab to die, and these are how you come back down.
1124
+ */
1125
+ const wanted = parameters.get('districts');
1126
+
1127
+ page.districts = manifest.districts
1128
+ .filter(district => wanted === null || wanted.split(',').includes(district.name))
1129
+ .map(district => ({ ...district, state: 'pending' }));
1130
+
1131
+ if (page.districts.length === 0) {
1132
+ throw new Error(`no district matched ?districts=${wanted}`);
1133
+ }
1134
+
1135
+ const placement_budget = Number(parameters.get('placements') ?? Number.POSITIVE_INFINITY);
1136
+
1137
+ /*
1138
+ `?vgeo=off` moves the *promoted* geometries back onto their meshlet blobs. It cannot move the
1139
+ merged assets, which have no blobs to move to — so the runtime is enabled whenever the set holds
1140
+ either kind, and only the promotion is a choice.
1141
+ */
1142
+ page.virtual_enabled = parameters.get('vgeo') !== 'off' && (manifest.vgeo?.length ?? 0) > 0;
1143
+
1144
+ const has_virtual = page.virtual_enabled || (manifest.merged?.length ?? 0) > 0;
1145
+
1146
+ const box = manifest.world_bounds;
1147
+
1148
+ page.world_radius = 0.5 * Math.hypot(box[3] - box[0], box[4] - box[1], box[5] - box[2]);
1149
+
1150
+ const renderer = new Renderer();
1151
+
1152
+ page.renderer = renderer;
1153
+
1154
+ await renderer.initialize({
1155
+ context: canvas.getContext('webgpu'),
1156
+ pixelRatio: globalThis.devicePixelRatio || 1,
1157
+ });
1158
+
1159
+ /*
1160
+ TAA on, SSAO and bloom off. The jitter is what keeps a scene made almost entirely of small
1161
+ hard-edged geometry from crawling; the other two are picture rather than geometry and this page
1162
+ is about geometry, so they are frame time spent on the wrong question.
1163
+ */
1164
+ renderer.feature_taa_enabled = true;
1165
+ renderer.feature_ssao_enabled = false;
1166
+ renderer.feature_bloom_enabled = false;
1167
+
1168
+ const scene = new Scene();
1169
+
1170
+ scene.dataset = new EntityComponentDataset();
1171
+
1172
+ page.scene = scene;
1173
+
1174
+ /*
1175
+ A sun roughly overhead and from the south-west, which is where Caldera's own lighting layer
1176
+ puts it. The set carries no lights — the conversion takes geometry and the source has no
1177
+ materials to begin with — so which way this points decides whether half the map is in ambient.
1178
+ */
1179
+ place_light(scene, make_sunlight({ direction: new Vector3(0.4, -0.82, 0.41) }));
1180
+
1181
+ /*
1182
+ Image-based lighting, for the geometry rather than for the picture: a sun alone puts every
1183
+ surface facing away from it at ambient zero, and those are the silhouettes worth looking at.
1184
+ Not awaited — `test_assets/` is gitignored, so a checkout without it gets the sun alone.
1185
+ */
1186
+ load_default_environment(scene);
1187
+
1188
+ const camera = new PerspectiveCamera();
1189
+
1190
+ camera.fov_degrees = 60;
1191
+
1192
+ page.camera = camera;
1193
+ page.controller = new OrbitalCameraController(camera, document.body);
1194
+
1195
+ /*
1196
+ The camera opens on the first district asked for rather than on the whole map. Caldera is two
1197
+ miles across and almost all of it is terrain; framed on the world box, a viewer opens three
1198
+ kilometres up looking at a grey lozenge, which is a true picture of the extent and tells you
1199
+ nothing about the geometry.
1200
+ */
1201
+ const framed = page.districts[0];
1202
+
1203
+ frame_on(page, framed.placements > 0 ? framed.bounds : box);
1204
+
1205
+ page.fit();
1206
+
1207
+ globalThis.addEventListener('resize', () => page.fit());
1208
+
1209
+ // one frame to let the renderer build its scene context, which is what owns the geometry manager
1210
+ renderer.render(camera, scene, 1 / 60);
1211
+
1212
+ const scene_context = renderer.scenes.obtain(scene);
1213
+
1214
+ page.scene_context = scene_context;
1215
+
1216
+ if (has_virtual) {
1217
+ enable_virtual_geometry(page, manifest, parameters);
1218
+ }
1219
+
1220
+ /*
1221
+ What the rasterization collections are primed to expect. Both grow on their own from what the
1222
+ frame reports, so this is about the first seconds rather than the steady state — but the first
1223
+ seconds are when the map is being placed, and a `Meshes` limit of 1e4 against four million rows
1224
+ counts the difference as `instances_dropped` for as long as it takes to catch up.
1225
+ */
1226
+ const limits = renderer.graphics.collection_limits;
1227
+
1228
+ limits.prime(
1229
+ RasterizationCollectionType.Meshes,
1230
+ Math.max(1e4, Math.ceil(Math.min(manifest.placements, placement_budget) * 0.25))
1231
+ );
1232
+
1233
+ limits.prime(RasterizationCollectionType.Meshlets, 1e6);
1234
+
1235
+ const material = new StandardShadeMaterial();
1236
+
1237
+ material.diffuse_color.copy(new Color(0.70, 0.69, 0.66));
1238
+ material.roughness_factor = 0.62;
1239
+ material.metallic_factor = 0;
1240
+
1241
+ /*
1242
+ `?side=front|back|double` sets the whole map's draw side, which is the cheapest way to ask
1243
+ whether what you are looking at is a winding problem: under `double` nothing is culled, so a
1244
+ surface that appears there and not under `front` was being culled rather than being absent.
1245
+ */
1246
+ material.draw_side = DRAW_SIDES[parameters.get('side')] ?? ShadeDrawSide.Front;
1247
+
1248
+ page.material = material;
1249
+
1250
+ install_profile_hotkey({
1251
+ renderer,
1252
+ name: "caldera",
1253
+ status: element('profile-status'),
1254
+ settings: () => {
1255
+ const state = page.state();
1256
+
1257
+ return [
1258
+ state.scene.districts_declared === state.scene.districts
1259
+ ? `${state.scene.districts} districts`
1260
+ : `${state.scene.districts} of ${state.scene.districts_declared} districts`,
1261
+ `${state.scene.placements} placements`,
1262
+ `${state.scene.geometries + state.scene.geometries_virtual} geometries`,
1263
+ page.virtual_enabled ? `vgeo on, τ=${state.cut?.tau}px` : 'vgeo off',
1264
+ `${state.screen_width}×${state.screen_height}`,
1265
+ ];
1266
+ },
1267
+ });
1268
+
1269
+ // a drag on a control in the panel is a drag on the body, and the camera controller listens there
1270
+ element('panel').addEventListener('pointerdown', event => event.stopPropagation());
1271
+
1272
+ page.paint_districts();
1273
+
1274
+ /*
1275
+ What a headless session reads. `__state` is the panel's own numbers, so a screenshot and a probe
1276
+ can never disagree about what the frame did; `__look` is how a scripted tour moves.
1277
+ */
1278
+ globalThis.__page = page;
1279
+ globalThis.__state = () => page.state();
1280
+ globalThis.__look = (eye, target) => page.look(eye, target);
1281
+
1282
+ let stopped = false;
1283
+ let last_time;
1284
+
1285
+ const tick = time => {
1286
+ if (stopped) {
1287
+ return;
1288
+ }
1289
+
1290
+ const dt = last_time === undefined ? 1 / 60 : Math.min((time - last_time) / 1000, 0.05);
1291
+
1292
+ if (last_time !== undefined) {
1293
+ page.frame_ms += ((time - last_time) - page.frame_ms) * 0.1;
1294
+ }
1295
+
1296
+ last_time = time;
1297
+
1298
+ const cpu_started = performance.now();
1299
+
1300
+ try {
1301
+ page.controller.update(dt);
1302
+
1303
+ renderer.render(camera, scene, dt);
1304
+ } catch (error) {
1305
+ stopped = true;
1306
+
1307
+ page.fatal = error;
1308
+
1309
+ page.status(
1310
+ `${error.message ?? String(error)}`
1311
+ + ` — stopped at ${page.placed.toLocaleString()} placements`
1312
+ + ` and ${(page.geometries.size + page.virtual.size).toLocaleString()} geometries`,
1313
+ true
1314
+ );
1315
+
1316
+ console.error(error);
1317
+
1318
+ return;
1319
+ }
1320
+
1321
+ page.cpu_ms += ((performance.now() - cpu_started) - page.cpu_ms) * 0.1;
1322
+ page.frames++;
1323
+
1324
+ paint(page);
1325
+
1326
+ requestAnimationFrame(tick);
1327
+ };
1328
+
1329
+ requestAnimationFrame(tick);
1330
+
1331
+ page.status(
1332
+ `opening ${page.districts.length} district${page.districts.length === 1 ? '' : 's'}…`
1333
+ );
1334
+
1335
+ /*
1336
+ Awaited, unlike the frame loop above it, because there is nothing after it but the summary. The
1337
+ scene arrives under a camera that is already drawing, which is the behaviour worth watching: a
1338
+ district that has not landed is simply not in the frame, and the frame does not wait for it.
1339
+ */
1340
+ await page.open_districts(placement_budget);
1341
+
1342
+ if (page.fatal === undefined) {
1343
+ page.status(
1344
+ `${page.placed.toLocaleString()} placements`
1345
+ + `, ${(page.geometries.size + page.virtual.size).toLocaleString()} geometries`
1346
+ + `, ${human_bytes(page.bytes_fetched)} fetched`
1347
+ + `, opened in ${(page.open_ms / 1000).toFixed(1)} s`
1348
+ + (page.refused > 0 ? ` — ${page.refused} refused` : '')
1349
+ );
1350
+ }
1351
+
1352
+ globalThis.__opened = true;
1353
+ }
1354
+
1355
+
1356
+ /**
1357
+ * Point the camera at a box, from far enough out to see all of it.
1358
+ *
1359
+ * Down and along rather than straight down: a map is read from an angle, and a plan view of one made
1360
+ * of buildings is a picture of their roofs.
1361
+ *
1362
+ * @param {CalderaPlayground} page
1363
+ * @param {number[]} box
1364
+ */
1365
+ function frame_on(page, box) {
1366
+ const centre = [
1367
+ (box[0] + box[3]) / 2, (box[1] + box[4]) / 2, (box[2] + box[5]) / 2,
1368
+ ];
1369
+
1370
+ const radius = Math.max(
1371
+ 10, 0.5 * Math.hypot(box[3] - box[0], box[4] - box[1], box[5] - box[2])
1372
+ );
1373
+
1374
+ page.look(
1375
+ [centre[0] + radius * 0.9, centre[1] + radius * 0.55, centre[2] + radius * 0.9],
1376
+ centre
1377
+ );
1378
+ }
1379
+
1380
+
1381
+ /**
1382
+ * Turn on virtual geometry for the containers the set promoted, and size its tables for them.
1383
+ *
1384
+ * @param {CalderaPlayground} page
1385
+ * @param {object} manifest
1386
+ * @param {URLSearchParams} parameters
1387
+ */
1388
+ function enable_virtual_geometry(page, manifest, parameters) {
1389
+ const page_capacity = Number(parameters.get('pages') ?? 16384);
1390
+
1391
+ const capacities = virtual_capacities(
1392
+ manifest, page_capacity, Number(parameters.get('queue') ?? 0)
1393
+ );
1394
+
1395
+ page.capacities = capacities;
1396
+
1397
+ const residency = new VGeoResidencyOptions();
1398
+
1399
+ residency.ram_budget_bytes = 2 * GIGABYTE;
1400
+
1401
+ /*
1402
+ VRAM is a strict subset of RAM, so the RAM tier has to hold the whole VRAM tier and every
1403
+ container's pinned root besides.
1404
+ */
1405
+ residency.ram_slot_capacity = Math.max(
1406
+ 1 << 14,
1407
+ (page_capacity + (manifest.vgeo?.length ?? 0) + (manifest.merged?.length ?? 0)) * 2
1408
+ );
1409
+
1410
+ residency.max_fetches_per_reading = 64;
1411
+ residency.max_concurrent_fetches = 24;
1412
+ residency.max_promotions_per_reading = 128;
1413
+
1414
+ page.runtime = page.scene_context.enable_virtual_geometry({
1415
+ page_size: manifest.vgeo_page_size ?? manifest.merged?.[0]?.page_size,
1416
+ table_options: {
1417
+ page_capacity,
1418
+ geometry_capacity: capacities.geometry_capacity,
1419
+ lookup_words: capacities.lookup_words,
1420
+ },
1421
+ claim_word_capacity: capacities.claim_word_capacity,
1422
+ want_capacity: 8192,
1423
+ /*
1424
+ Traversal items one wavefront may hold, over the engine's 1 << 16. This is the capacity a
1425
+ scene reaches first and its overflow is invisible without looking: the frontier is simply
1426
+ thrown away, and what you see is the drawn cluster count wandering between frames as
1427
+ different subtrees lose the race.
1428
+
1429
+ It is derived from the instance count rather than fixed, because the descent is seeded with
1430
+ one item per visible virtual instance and that number is a property of the *set*: at the
1431
+ default 20,000-triangle promotion Caldera has 12,147 virtual instances, and at 4,096 it has
1432
+ 172,764. A constant that is ample for one is a fourteenth of the other. Zorah settled at
1433
+ 1 << 18 for 17,424 instances, which is where the factor of sixteen comes from.
1434
+ */
1435
+ queue_capacity: capacities.queue_capacity,
1436
+ residency,
1437
+ });
1438
+
1439
+ page.residency = page.scene_context.virtual_geometry_residency;
1440
+ page.feedback = page.scene_context.virtual_geometry_feedback;
1441
+
1442
+ globalThis.addEventListener('pagehide', () => page.feedback.destroy());
1443
+
1444
+ page.renderer.add_extension(new VGeoFrameStart(page));
1445
+ page.renderer.add_extension(new VGeoFrameEnd(page));
1446
+
1447
+ const tau = element('tau');
1448
+
1449
+ tau.addEventListener('input', event => {
1450
+ page.runtime.tau = Number(event.target.value);
1451
+
1452
+ element('tau-value').textContent = page.runtime.tau.toFixed(2);
1453
+ });
1454
+
1455
+ const requested = Number(parameters.get('tau'));
1456
+
1457
+ if (Number.isFinite(requested) && requested > 0) {
1458
+ page.runtime.tau = requested;
1459
+
1460
+ tau.value = String(requested);
1461
+ element('tau-value').textContent = requested.toFixed(2);
1462
+ }
1463
+ }
1464
+
1465
+
1466
+ /**
1467
+ * @type {number}
1468
+ */
1469
+ let painted_at = 0;
1470
+
1471
+
1472
+ /**
1473
+ * @param {CalderaPlayground} page
1474
+ */
1475
+ function paint(page) {
1476
+ const now = performance.now();
1477
+
1478
+ // the panel is DOM and the scene is four million rows; repainting it every frame is a
1479
+ // measurable share of a frame that this page is trying to measure
1480
+ if (now - painted_at < 250) {
1481
+ return;
1482
+ }
1483
+
1484
+ painted_at = now;
1485
+
1486
+ const state = page.state();
1487
+ const scene = state.scene;
1488
+
1489
+ set('s-districts', `${scene.districts} / ${scene.districts_declared}`);
1490
+ set(
1491
+ 's-placements',
1492
+ scene.placements.toLocaleString()
1493
+ + (scene.placements > ROWS_LAST_KNOWN_GOOD ? ' ⚠ past measured VRAM' : ''),
1494
+ scene.placements > ROWS_LAST_KNOWN_GOOD ? 'bad' : ''
1495
+ );
1496
+ set('s-geometries', scene.geometries_virtual > 0
1497
+ ? `${scene.geometries.toLocaleString()} + ${scene.geometries_virtual.toLocaleString()} vg`
1498
+ : scene.geometries.toLocaleString());
1499
+ set('s-source', scene.source_triangles.toLocaleString());
1500
+ set('s-instanced', scene.instanced_triangles.toLocaleString());
1501
+ set('s-fetched', human_bytes(scene.bytes_fetched));
1502
+ set('s-open', scene.open_ms > 0 ? `${(scene.open_ms / 1000).toFixed(1)} s` : '—');
1503
+
1504
+ set('f-frame', `${state.frame_ms.toFixed(1)} ms`);
1505
+ set('f-cpu', `${state.cpu_ms.toFixed(1)} ms`);
1506
+ set('f-resolution', `${state.screen_width}×${state.screen_height}`);
1507
+ set('f-meshes', state.collections.meshes.toLocaleString());
1508
+ set('f-meshlets', state.collections.meshlets.toLocaleString());
1509
+
1510
+ /*
1511
+ Virtual geometry's drops only — the ordinary meshlet path publishes none, which is exactly why
1512
+ the 2^20 failure above was invisible. A zero here is not a statement that the frame is complete.
1513
+
1514
+ `queue_dropped` is in the sum because it is the one that degrades *quietly*: an overflowed
1515
+ frontier is thrown away rather than refused, and what you see is the drawn cluster count
1516
+ wandering between frames as different subtrees lose the race. It reads as instability in the
1517
+ cut rather than as a capacity that was too small.
1518
+ */
1519
+ const dropped = (state.cut?.instances_dropped ?? 0)
1520
+ + (state.cut?.meshlets_dropped ?? 0)
1521
+ + (state.cut?.queue_dropped ?? 0);
1522
+
1523
+ set('f-dropped', dropped.toLocaleString(), dropped > 0 ? 'bad' : 'good');
1524
+
1525
+ if (state.cut === undefined) {
1526
+ set('v-containers', 'off', 'muted');
1527
+
1528
+ return;
1529
+ }
1530
+
1531
+ set('v-containers', scene.merged > 0
1532
+ ? `${scene.geometries_virtual.toLocaleString()} (${scene.merged} merged)`
1533
+ : scene.geometries_virtual.toLocaleString());
1534
+ set('v-instances', scene.placements_virtual.toLocaleString());
1535
+ set('v-drawn', state.cut.drawn.toLocaleString());
1536
+ set('v-pages', `${state.vram.pages.toLocaleString()} / ${state.vram.capacity.toLocaleString()}`);
1537
+
1538
+ const strained = state.vram.refused + state.cut.clamped;
1539
+
1540
+ set('v-refused', `${state.vram.refused.toLocaleString()} / ${state.cut.clamped.toLocaleString()}`,
1541
+ strained > 0 ? 'warn' : 'good');
1542
+ }
1543
+
1544
+
1545
+ /**
1546
+ * @param {string} id
1547
+ * @param {string} text
1548
+ * @param {string} [tone]
1549
+ */
1550
+ function set(id, text, tone) {
1551
+ const node = element(id);
1552
+
1553
+ node.textContent = text;
1554
+ node.className = tone ?? '';
1555
+ }
1556
+
1557
+
1558
+ start().catch(error => {
1559
+ console.error(error);
1560
+
1561
+ const node = element('status');
1562
+
1563
+ node.textContent = error.message ?? String(error);
1564
+ node.classList.add('error');
1565
+ });