@woosh/meep-engine 3.27.0 → 3.28.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 (255) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts +15 -0
  4. package/src/core/geom/3d/mat4/m4_linear_determinant.d.ts.map +1 -0
  5. package/src/core/geom/3d/mat4/m4_linear_determinant.js +24 -0
  6. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts +4 -2
  7. package/src/core/geom/3d/mat4/m4_normal_matrix3.d.ts.map +1 -1
  8. package/src/core/geom/3d/mat4/m4_normal_matrix3.js +21 -11
  9. package/src/engine/Engine.d.ts.map +1 -1
  10. package/src/engine/Engine.js +38 -1
  11. package/src/engine/graphics3/GraphicsEngine.d.ts +5 -1
  12. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  13. package/src/engine/graphics3/GraphicsEngine.js +834 -830
  14. package/src/shade/RENDERER_CONTRACT.md +19 -10
  15. package/src/shade/device/timing/GPU_PROFILER_PROPOSAL_2026_08_28.md +1 -1
  16. package/src/shade/playground/vgeo_runtime/README.md +27 -13
  17. package/src/shade/playground/vgeo_runtime/main.js +18 -5
  18. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts +18 -0
  19. package/src/shade/playground/vgeo_runtime/sample_asset.d.ts.map +1 -0
  20. package/src/shade/playground/{vgeo_viewer → vgeo_runtime}/sample_asset.js +9 -8
  21. package/src/shade/playground/vgeo_scene/README.md +408 -0
  22. package/src/shade/playground/vgeo_scene/index.html +144 -0
  23. package/src/shade/playground/vgeo_scene/main.d.ts +2 -0
  24. package/src/shade/playground/vgeo_scene/main.d.ts.map +1 -0
  25. package/src/shade/playground/vgeo_scene/main.js +1716 -0
  26. package/src/shade/renderer/Renderer.d.ts +4 -1
  27. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  28. package/src/shade/renderer/Renderer.js +5 -2
  29. package/src/shade/renderer/extension/RENDER_EXTENSION_DESIGN.md +1 -2
  30. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  31. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +3 -29
  32. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts +22 -0
  33. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.d.ts.map +1 -0
  34. package/src/shade/renderer/geometry/meshlet/encoding/ENCODED_ATTRIBUTE_DEFAULT_VALUES.js +49 -0
  35. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +4 -2
  36. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +10 -8
  37. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +12 -11
  38. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +1 -1
  39. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  40. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +1 -1
  41. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.d.ts.map +1 -1
  42. package/src/shade/renderer/geometry/virtual/build/gltf/gltf_open_document.js +5 -9
  43. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts +32 -0
  44. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevel.d.ts.map +1 -0
  45. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts +59 -0
  46. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelOptions.d.ts.map +1 -0
  47. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts +44 -0
  48. package/src/shade/renderer/geometry/virtual/build/level/VGeoLevelSummary.d.ts.map +1 -0
  49. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts +36 -0
  50. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_levels.d.ts.map +1 -0
  51. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_levels.js → level/vgeo_build_levels.js} +61 -95
  52. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts +19 -0
  53. package/src/shade/renderer/geometry/virtual/build/level/vgeo_build_root_group.d.ts.map +1 -0
  54. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_root_group.js → level/vgeo_build_root_group.js} +1 -1
  55. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts +73 -0
  56. package/src/shade/renderer/geometry/virtual/build/mesh/VirtualGeometryMesh.d.ts.map +1 -0
  57. package/src/shade/renderer/geometry/virtual/build/{VirtualGeometryMesh.js → mesh/VirtualGeometryMesh.js} +7 -7
  58. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts +29 -0
  59. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_normals.d.ts.map +1 -0
  60. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_normals.js → mesh/vgeo_compute_vertex_normals.js} +3 -3
  61. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts +31 -0
  62. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_compute_vertex_tangents.d.ts.map +1 -0
  63. package/src/shade/renderer/geometry/virtual/build/{vgeo_compute_vertex_tangents.js → mesh/vgeo_compute_vertex_tangents.js} +5 -5
  64. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts +46 -0
  65. package/src/shade/renderer/geometry/virtual/build/mesh/vgeo_normalize_source.d.ts.map +1 -0
  66. package/src/shade/renderer/geometry/virtual/build/{vgeo_normalize_source.js → mesh/vgeo_normalize_source.js} +9 -9
  67. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts +13 -0
  68. package/src/shade/renderer/geometry/virtual/build/page/vgeo_assemble_pages.d.ts.map +1 -0
  69. package/src/shade/renderer/geometry/virtual/build/{vgeo_assemble_pages.js → page/vgeo_assemble_pages.js} +9 -9
  70. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts +13 -0
  71. package/src/shade/renderer/geometry/virtual/build/page/vgeo_build_page_nodes.d.ts.map +1 -0
  72. package/src/shade/renderer/geometry/virtual/build/{vgeo_build_page_nodes.js → page/vgeo_build_page_nodes.js} +4 -4
  73. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts +22 -0
  74. package/src/shade/renderer/geometry/virtual/build/page/vgeo_page_node_count.d.ts.map +1 -0
  75. package/src/shade/renderer/geometry/virtual/build/{vgeo_page_node_count.js → page/vgeo_page_node_count.js} +2 -2
  76. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts +52 -0
  77. package/src/shade/renderer/geometry/virtual/build/partition/VGeoFaceGraph.d.ts.map +1 -0
  78. package/src/shade/renderer/geometry/virtual/build/{VGeoFaceGraph.js → partition/VGeoFaceGraph.js} +1 -1
  79. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts +14 -0
  80. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_graph_components.d.ts.map +1 -0
  81. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts +29 -0
  82. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_graph.d.ts.map +1 -0
  83. package/src/shade/renderer/geometry/virtual/build/{vgeo_partition_graph.js → partition/vgeo_partition_graph.js} +2 -2
  84. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts +41 -0
  85. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.d.ts.map +1 -0
  86. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_partition_subgraph.js +92 -0
  87. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts +10 -0
  88. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set.d.ts.map +1 -0
  89. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set.js → partition/vgeo_split_face_set.js} +12 -55
  90. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts +15 -0
  91. package/src/shade/renderer/geometry/virtual/build/partition/vgeo_split_face_set_packed.d.ts.map +1 -0
  92. package/src/shade/renderer/geometry/virtual/build/{vgeo_split_face_set_packed.js → partition/vgeo_split_face_set_packed.js} +3 -3
  93. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts +12 -0
  94. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_bounding_sphere.d.ts.map +1 -0
  95. package/src/shade/renderer/geometry/virtual/build/{vgeo_bounding_sphere.js → sphere/vgeo_bounding_sphere.js} +3 -3
  96. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts +18 -0
  97. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_containing_sphere.d.ts.map +1 -0
  98. package/src/shade/renderer/geometry/virtual/build/{vgeo_containing_sphere.js → sphere/vgeo_containing_sphere.js} +3 -3
  99. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts +9 -0
  100. package/src/shade/renderer/geometry/virtual/build/sphere/vgeo_sphere_inflate_to_contain.d.ts.map +1 -0
  101. package/src/shade/renderer/geometry/virtual/build/{vgeo_sphere_inflate_to_contain.js → sphere/vgeo_sphere_inflate_to_contain.js} +1 -1
  102. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts +24 -0
  103. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoGatheredFaces.d.ts.map +1 -0
  104. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts +20 -0
  105. package/src/shade/renderer/geometry/virtual/build/submesh/VGeoSubmesh.d.ts.map +1 -0
  106. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts +14 -0
  107. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_extract_submesh.d.ts.map +1 -0
  108. package/src/shade/renderer/geometry/virtual/build/{vgeo_extract_submesh.js → submesh/vgeo_extract_submesh.js} +5 -5
  109. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts +12 -0
  110. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_gather_faces.d.ts.map +1 -0
  111. package/src/shade/renderer/geometry/virtual/build/{vgeo_gather_faces.js → submesh/vgeo_gather_faces.js} +2 -2
  112. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts +11 -0
  113. package/src/shade/renderer/geometry/virtual/build/submesh/vgeo_narrow_indices.d.ts.map +1 -0
  114. package/src/shade/renderer/geometry/virtual/build/{vgeo_narrow_indices.js → submesh/vgeo_narrow_indices.js} +3 -3
  115. package/src/shade/renderer/geometry/virtual/build/vgeo_build.d.ts.map +1 -1
  116. package/src/shade/renderer/geometry/virtual/build/vgeo_build.js +9 -11
  117. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts +8 -2
  118. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.d.ts.map +1 -1
  119. package/src/shade/renderer/geometry/virtual/format/attribute/vgeo_attributes_read.js +8 -2
  120. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts +15 -0
  121. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/format/page/VGEO_GROUP_OFFSET.js +15 -0
  123. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts +28 -0
  124. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.d.ts.map +1 -1
  125. package/src/shade/renderer/geometry/virtual/format/payload/vgeo_encode_cluster_payload.js +7 -3
  126. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  127. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +9 -4
  128. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  129. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +18 -5
  130. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  131. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +9 -1
  132. package/src/shade/renderer/geometry/virtual/runtime/CUT_PLAN_2026_09_14.md +21 -2
  133. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +52 -2
  134. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -1
  135. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +97 -1
  136. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +4 -4
  137. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -1
  138. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +10 -0
  139. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.d.ts.map +1 -1
  140. package/src/shade/renderer/geometry/virtual/runtime/VGeoResidency.js +59 -34
  141. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +8 -8
  142. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -1
  143. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +11 -88
  144. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts +19 -0
  145. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.d.ts.map +1 -0
  146. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_arena.js +98 -0
  147. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts +31 -0
  148. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.d.ts.map +1 -0
  149. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_claim.js +99 -0
  150. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts +17 -0
  151. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.d.ts.map +1 -0
  152. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js +39 -0
  153. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.d.ts.map +1 -1
  154. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js +7 -63
  155. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -1
  156. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +13 -30
  157. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -1
  158. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +6 -52
  159. package/src/shade/renderer/loader/gltf/load_gltf.d.ts.map +1 -1
  160. package/src/shade/renderer/loader/gltf/load_gltf.js +0 -19
  161. package/src/shade/renderer/material/chunk_material_draw_side.d.ts +14 -0
  162. package/src/shade/renderer/material/chunk_material_draw_side.d.ts.map +1 -0
  163. package/src/shade/renderer/material/chunk_material_draw_side.js +34 -0
  164. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  165. package/src/shade/renderer/material/standard/fragment_gbuffer.js +8 -1
  166. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.d.ts.map +1 -1
  167. package/src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js +13 -1
  168. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts +19 -5
  169. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.d.ts.map +1 -1
  170. package/src/shade/renderer/rasterize/bucket/construct_primitive_state.js +23 -5
  171. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.d.ts.map +1 -1
  172. package/src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js +12 -5
  173. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts +22 -0
  174. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.d.ts.map +1 -0
  175. package/src/shade/renderer/rasterize/bucket/rasterization_opaque_buckets.js +36 -0
  176. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts +15 -0
  177. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.d.ts.map +1 -0
  178. package/src/shade/renderer/rasterize/chunk_triangle_winding_is_reversed.js +35 -0
  179. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts +10 -1
  180. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.d.ts.map +1 -1
  181. package/src/shade/renderer/rasterize/expand/bucket/RASTERIZATION_BUCKET_LIMIT.js +38 -29
  182. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts +5 -0
  183. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.d.ts.map +1 -1
  184. package/src/shade/renderer/rasterize/expand/bucket/chunk_rasterization_material_bucket.js +63 -55
  185. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  186. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +1 -0
  187. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  188. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +44 -53
  189. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.d.ts.map +1 -1
  190. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_brick4.js +6 -2
  191. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.d.ts.map +1 -1
  192. package/src/shade/renderer/rasterize/forward/chunk_forward_shade_standard_fragment_ibl.js +6 -2
  193. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.d.ts.map +1 -1
  194. package/src/shade/renderer/rasterize/forward/vertex_shader_chunk.js +19 -2
  195. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_PLAN_2026_09_03.md +7 -4
  196. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  197. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +3 -2
  198. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.d.ts.map +1 -1
  199. package/src/shade/renderer/rasterize/native/chunk_rasterize_triangle.js +15 -4
  200. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  201. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_alpha_tested_pass_descriptor.js +16 -5
  202. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.d.ts.map +1 -1
  203. package/src/shade/renderer/rasterize/native/depth/shadow_rasterization_pass_descriptor.js +16 -5
  204. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  205. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +2 -1
  206. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.d.ts.map +1 -1
  207. package/src/shade/renderer/rasterize/native/rasterization_shader_resources.js +4 -0
  208. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.d.ts.map +1 -1
  209. package/src/shade/renderer/rasterize/native/viz/viz_rasterization_alpha_tested_pass_descriptor.js +16 -7
  210. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  211. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +78 -88
  212. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  213. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +21 -31
  214. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  215. package/src/shade/renderer/scene/GPUSceneContext.js +2 -0
  216. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts +19 -0
  217. package/src/shade/renderer/scene/NODE3D_STRUCT.d.ts.map +1 -1
  218. package/src/shade/renderer/scene/NODE3D_STRUCT.js +34 -0
  219. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts +9 -0
  220. package/src/shade/renderer/scene/chunk_node3d_flags.d.ts.map +1 -0
  221. package/src/shade/renderer/scene/chunk_node3d_flags.js +23 -0
  222. package/src/shade/renderer/scene/chunk_scene_database_access.d.ts.map +1 -1
  223. package/src/shade/renderer/scene/chunk_scene_database_access.js +15 -1
  224. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  225. package/src/shade/renderer/scene/rows/GPUSceneRows.js +9 -1
  226. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts +7 -0
  227. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.d.ts.map +1 -1
  228. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js +16 -4
  229. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts +12 -0
  230. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.d.ts.map +1 -0
  231. package/src/shade/renderer/shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js +18 -0
  232. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts +24 -0
  233. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.d.ts.map +1 -0
  234. package/src/shade/renderer/shader/chunk/geometry/triangle/chunk_triangle_list_reverse_winding.js +33 -0
  235. package/src/shade/playground/vgeo_viewer/README.md +0 -150
  236. package/src/shade/playground/vgeo_viewer/cut_geometry.js +0 -460
  237. package/src/shade/playground/vgeo_viewer/index.html +0 -77
  238. package/src/shade/playground/vgeo_viewer/main.js +0 -1343
  239. package/src/shade/playground/vgeo_viewer/select_cut.js +0 -481
  240. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +0 -258
  241. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +0 -1
  242. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +0 -49
  243. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +0 -1
  244. package/src/shade/renderer/loader/gltf/fix_up_material_sides.js +0 -109
  245. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +0 -78
  246. package/src/shade/renderer/rasterize/expand/shader_filter_triangle_by_material_side.js +0 -100
  247. package/src/shade/renderer/rasterize/forward/chunk_material_side_should_draw.js +0 -26
  248. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts +0 -3
  249. package/src/shade/renderer/shader/chunk/jobs/chunk_job_system.d.ts.map +0 -1
  250. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevel.js → level/VGeoLevel.js} +0 -0
  251. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelOptions.js → level/VGeoLevelOptions.js} +0 -0
  252. /package/src/shade/renderer/geometry/virtual/build/{VGeoLevelSummary.js → level/VGeoLevelSummary.js} +0 -0
  253. /package/src/shade/renderer/geometry/virtual/build/{vgeo_graph_components.js → partition/vgeo_graph_components.js} +0 -0
  254. /package/src/shade/renderer/geometry/virtual/build/{VGeoGatheredFaces.js → submesh/VGeoGatheredFaces.js} +0 -0
  255. /package/src/shade/renderer/geometry/virtual/build/{VGeoSubmesh.js → submesh/VGeoSubmesh.js} +0 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Put one page on the wavefront queue at its root node.
3
+ *
4
+ * Both passes that start a descent — the seed, from an instance's root page, and the reseed, from
5
+ * last frame's frontier — push the same four words and drop on the same bound, and both wrote it
6
+ * out. `shader_vgeo_cut_traverse` does not use this: it reserves a run for a whole workgroup's
7
+ * spill in one `atomicAdd` and writes into it, which is a different thing wearing the same words.
8
+ *
9
+ * Requires the importing shader to declare `params`, `queue_out` and `feedback`.
10
+ *
11
+ * @author Alex Goldring
12
+ * @copyright Company Named Limited (c) 2026
13
+ * @type {CodeChunk}
14
+ */
15
+ export const chunk_vgeo_cut_queue: CodeChunk;
16
+ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
17
+ //# sourceMappingURL=chunk_vgeo_cut_queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk_vgeo_cut_queue.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_queue.js"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,mCAFU,SAAS,CAsBQ;0BAtCD,0CAA0C"}
@@ -0,0 +1,39 @@
1
+ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
2
+ import { VGEO_CUT_STAT } from "../layout/VGEO_CUT_STAT.js";
3
+ import { chunk_vgeo_cut_claim } from "./chunk_vgeo_cut_claim.js";
4
+
5
+ /**
6
+ * Put one page on the wavefront queue at its root node.
7
+ *
8
+ * Both passes that start a descent — the seed, from an instance's root page, and the reseed, from
9
+ * last frame's frontier — push the same four words and drop on the same bound, and both wrote it
10
+ * out. `shader_vgeo_cut_traverse` does not use this: it reserves a run for a whole workgroup's
11
+ * spill in one `atomicAdd` and writes into it, which is a different thing wearing the same words.
12
+ *
13
+ * Requires the importing shader to declare `params`, `queue_out` and `feedback`.
14
+ *
15
+ * @author Alex Goldring
16
+ * @copyright Company Named Limited (c) 2026
17
+ * @type {CodeChunk}
18
+ */
19
+ export const chunk_vgeo_cut_queue = CodeChunk.from(
20
+ //language=WGSL
21
+ `
22
+ fn vg_queue_push_root(instance_slot: u32, page_row: u32) {
23
+ let slot = atomicAdd(&queue_out.count, 1u);
24
+
25
+ if (slot >= params.queue_capacity) {
26
+ vg_stat(${VGEO_CUT_STAT.QUEUE_DROPPED}u, 1u);
27
+
28
+ return;
29
+ }
30
+
31
+ let base = slot * 4u;
32
+
33
+ queue_out.elements[base] = instance_slot;
34
+ queue_out.elements[base + 1u] = page_row;
35
+ // node 0, which is how every page visit in this runtime begins
36
+ queue_out.elements[base + 2u] = 0u;
37
+ queue_out.elements[base + 3u] = 0u;
38
+ }
39
+ `, [chunk_vgeo_cut_claim]);
@@ -1 +1 @@
1
- {"version":3,"file":"shader_vgeo_cut_reseed.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js"],"names":[],"mappings":"AAqLA,mDAIG;8BAtL2B,qCAAqC"}
1
+ {"version":3,"file":"shader_vgeo_cut_reseed.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_reseed.js"],"names":[],"mappings":"AA6HA,mDAIG;8BA9H2B,qCAAqC"}
@@ -3,9 +3,9 @@ import { WebGPUArray } from "../../../../shader/type/WebGPUArray.js";
3
3
  import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
4
4
  import { ComputeShader } from "../../../../shader/ComputeShader.js";
5
5
  import { ShaderResourceSetDescriptor } from "../../../../shader/resource/ShaderResourceSetDescriptor.js";
6
+ import { chunk_vgeo_cut_claim } from "./chunk_vgeo_cut_claim.js";
7
+ import { chunk_vgeo_cut_queue } from "./chunk_vgeo_cut_queue.js";
6
8
  import { COLLECTION_U32_STRUCT_WRITE } from "../../../../gpu_primitive/COLLECTION_U32_STRUCT_WRITE.js";
7
- import { VGEO_CUT_STAT } from "../layout/VGEO_CUT_STAT.js";
8
- import { VGEO_FEEDBACK_OFFSET } from "../layout/VGEO_FEEDBACK_OFFSET.js";
9
9
  import { VGEO_INSTANCE_OFFSET } from "../layout/VGEO_INSTANCE_OFFSET.js";
10
10
  import { VGEO_INSTANCE_WORDS } from "../layout/VGEO_INSTANCE_WORDS.js";
11
11
  import { VGEO_PAGE_ROW_NONE } from "../layout/VGEO_PAGE_ROW_NONE.js";
@@ -70,46 +70,6 @@ resources.createGroup()
70
70
  const body = CodeChunk.from(
71
71
  //language=WGSL
72
72
  `
73
- /**
74
- * A field of a page's slot header. Indexed rather than read through the traversal's accessors,
75
- * because those come with a chunk that declares rather more than this pass binds.
76
- */
77
- fn vg_reseed_page_field(page_row: u32, field: u32) -> u32 {
78
- return vg_arena[page_row * params.page_slot_words + field];
79
- }
80
-
81
- /**
82
- * The claim, and the touch that goes with it — the seed pass's, taken against a page row that came
83
- * off a bitmask rather than off a reference already resolved.
84
- */
85
- fn vg_reseed_claim(instance_base: u32, page_row: u32) -> bool {
86
- let ordinal = vg_reseed_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.ORDINAL}u);
87
-
88
- if (ordinal >= instances[instance_base + ${VGEO_INSTANCE_OFFSET.ORDINAL_CAPACITY}u]) {
89
- return false;
90
- }
91
-
92
- let bit = instances[instance_base + ${VGEO_INSTANCE_OFFSET.CLAIM_BASE}u] + ordinal;
93
-
94
- let word = bit >> 5u;
95
-
96
- if (word >= params.claim_word_count) {
97
- return false;
98
- }
99
-
100
- let mask = 1u << (bit & 31u);
101
-
102
- if ((atomicOr(&claim[word], mask) & mask) != 0u) {
103
- return false;
104
- }
105
-
106
- atomicAdd(&feedback[${VGEO_FEEDBACK_OFFSET.STATS}u + ${VGEO_CUT_STAT.PAGES}u], 1u);
107
-
108
- atomicOr(&feedback[params.touch_bits_base + (page_row >> 5u)], 1u << (page_row & 31u));
109
-
110
- return true;
111
- }
112
-
113
73
  @compute @workgroup_size(${VGEO_CUT_WORKGROUP_SIZE})
114
74
  fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
115
75
  let rows = params.frontier_word_count * 32u;
@@ -145,8 +105,8 @@ fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
145
105
  would reject the ordinal eventually, but the geometry row is the honest test and it is two
146
106
  reads of a header that is already in cache.
147
107
  */
148
- if (vg_reseed_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.GEOMETRY_ROW}u)
149
- != vg_reseed_page_field(root_row, ${VGEO_PAGE_ROW_OFFSET.GEOMETRY_ROW}u)) {
108
+ if (vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.GEOMETRY_ROW}u)
109
+ != vg_page_field(root_row, ${VGEO_PAGE_ROW_OFFSET.GEOMETRY_ROW}u)) {
150
110
  return;
151
111
  }
152
112
 
@@ -155,29 +115,13 @@ fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
155
115
  which is the LRU's whole input, and it is what stops a page reached both from the frontier and
156
116
  from a descent later in this same cut from being drawn twice.
157
117
  */
158
- if (!vg_reseed_claim(instance_base, page_row)) {
118
+ if (!vg_claim_page(instance_base, page_row)) {
159
119
  return;
160
120
  }
161
121
 
162
- let slot = atomicAdd(&queue_out.count, 1u);
163
-
164
- if (slot >= params.queue_capacity) {
165
- atomicAdd(
166
- &feedback[${VGEO_FEEDBACK_OFFSET.STATS}u + ${VGEO_CUT_STAT.QUEUE_DROPPED}u], 1u
167
- );
168
-
169
- return;
170
- }
171
-
172
- let base = slot * 4u;
173
-
174
- queue_out.elements[base] = instance_slot;
175
- queue_out.elements[base + 1u] = page_row;
176
- // node 0, which is how every page visit in this runtime begins
177
- queue_out.elements[base + 2u] = 0u;
178
- queue_out.elements[base + 3u] = 0u;
122
+ vg_queue_push_root(instance_slot, page_row);
179
123
  }
180
- `, []);
124
+ `, [chunk_vgeo_cut_claim, chunk_vgeo_cut_queue]);
181
125
 
182
126
  export const shader_vgeo_cut_reseed = ComputeShader.from({
183
127
  label: "VGeo cut reseed",
@@ -1 +1 @@
1
- {"version":3,"file":"shader_vgeo_cut_seed.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js"],"names":[],"mappings":"AAuJA,iDAIG;8BAtJ2B,qCAAqC"}
1
+ {"version":3,"file":"shader_vgeo_cut_seed.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js"],"names":[],"mappings":"AAsIA,iDAIG;8BArI2B,qCAAqC"}
@@ -5,12 +5,13 @@ import { COLLECTION_U32_STRUCT_READ } from "../../../../gpu_primitive/COLLECTION
5
5
  import { COLLECTION_U32_STRUCT_WRITE } from "../../../../gpu_primitive/COLLECTION_U32_STRUCT_WRITE.js";
6
6
  import { ComputeShader } from "../../../../shader/ComputeShader.js";
7
7
  import { ShaderResourceSetDescriptor } from "../../../../shader/resource/ShaderResourceSetDescriptor.js";
8
+ import { chunk_vgeo_cut_claim } from "./chunk_vgeo_cut_claim.js";
9
+ import { chunk_vgeo_cut_queue } from "./chunk_vgeo_cut_queue.js";
8
10
  import { VGEO_CUT_STAT } from "../layout/VGEO_CUT_STAT.js";
9
11
  import { VGEO_FEEDBACK_OFFSET } from "../layout/VGEO_FEEDBACK_OFFSET.js";
10
12
  import { VGEO_INSTANCE_OFFSET } from "../layout/VGEO_INSTANCE_OFFSET.js";
11
13
  import { VGEO_INSTANCE_WORDS } from "../layout/VGEO_INSTANCE_WORDS.js";
12
14
  import { VGEO_PAGE_ROW_NONE } from "../layout/VGEO_PAGE_ROW_NONE.js";
13
- import { VGEO_PAGE_ROW_OFFSET } from "../layout/VGEO_PAGE_ROW_OFFSET.js";
14
15
  import { VGEO_CUT_PARAMS_STRUCT } from "./VGEO_CUT_PARAMS_STRUCT.js";
15
16
  import { VGEO_CUT_WORKGROUP_SIZE } from "./VGEO_CUT_WORKGROUP_SIZE.js";
16
17
 
@@ -118,36 +119,18 @@ fn main(@builtin(global_invocation_id) global_id: vec3<u32>) {
118
119
  because it is also what stamps the root page as touched, and a root that never reports a touch
119
120
  is a root that ages out of the touch-LRU while it is on screen.
120
121
  */
121
- let ordinal = vg_arena[
122
- page_row * params.page_slot_words + ${VGEO_PAGE_ROW_OFFSET.ORDINAL}u
123
- ];
124
-
125
- let bit = claim_base + ordinal;
126
-
127
- atomicOr(&claim[bit >> 5u], 1u << (bit & 31u));
128
-
129
- atomicAdd(&feedback[${VGEO_FEEDBACK_OFFSET.STATS}u + ${VGEO_CUT_STAT.PAGES}u], 1u);
130
-
131
- atomicOr(&feedback[params.touch_bits_base + (page_row >> 5u)], 1u << (page_row & 31u));
132
-
133
- let slot = atomicAdd(&queue_out.count, 1u);
134
-
135
- if (slot >= params.queue_capacity) {
136
- atomicAdd(
137
- &feedback[${VGEO_FEEDBACK_OFFSET.STATS}u + ${VGEO_CUT_STAT.QUEUE_DROPPED}u], 1u
138
- );
139
-
140
- return;
141
- }
142
-
143
- let base = slot * 4u;
144
-
145
- queue_out.elements[base] = instance_slot;
146
- queue_out.elements[base + 1u] = page_row;
147
- queue_out.elements[base + 2u] = 0u;
148
- queue_out.elements[base + 3u] = 0u;
122
+ /*
123
+ Through the shared claim, and by slice rather than by instance record: the claim base was
124
+ written to the instance record two lines above and is still in a register here, so the
125
+ _at form spares the read back. The claim cannot fail — the root is pinned and the capacity
126
+ bound was taken above — but it is the same code the descent and the reseed run, which is
127
+ the point of taking it rather than open-coding the three atomics.
128
+ */
129
+ vg_claim_page_at(claim_base, capacity, page_row);
130
+
131
+ vg_queue_push_root(instance_slot, page_row);
149
132
  }
150
- `, []);
133
+ `, [chunk_vgeo_cut_claim, chunk_vgeo_cut_queue]);
151
134
 
152
135
  export const shader_vgeo_cut_seed = ComputeShader.from({
153
136
  label: "VGeo cut seed",
@@ -1 +1 @@
1
- {"version":3,"file":"shader_vgeo_cut_traverse.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js"],"names":[],"mappings":"AAq5BA,qDAIG;8BAt5B2B,qCAAqC"}
1
+ {"version":3,"file":"shader_vgeo_cut_traverse.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js"],"names":[],"mappings":"AAu2BA,qDAIG;8BAx2B2B,qCAAqC"}
@@ -23,6 +23,7 @@ import { VGEO_QUEUE_TARGET_PARENT_BIT } from "../layout/VGEO_QUEUE_TARGET_PARENT
23
23
  import { VGEO_QUEUE_TARGET_GROUP_BIT } from "../layout/VGEO_QUEUE_TARGET_GROUP_BIT.js";
24
24
  import { VGEO_WANT_WORDS } from "../layout/VGEO_WANT_WORDS.js";
25
25
  import { chunk_vgeo_cut_access } from "./chunk_vgeo_cut_access.js";
26
+ import { chunk_vgeo_cut_claim } from "./chunk_vgeo_cut_claim.js";
26
27
  import { VGEO_CUT_EXPAND } from "./VGEO_CUT_EXPAND.js";
27
28
  import { VGEO_CUT_FLAG_COARSEN } from "./VGEO_CUT_FLAG_COARSEN.js";
28
29
  import { VGEO_CUT_FLAG_CULL } from "./VGEO_CUT_FLAG_CULL.js";
@@ -237,58 +238,10 @@ fn vg_expand_none() -> VGExpand {
237
238
  return VGExpand(VG_EXPAND_NONE, 0u, 0u, 0u, 0u);
238
239
  }
239
240
 
240
- fn vg_stat(slot: u32, amount: u32) {
241
- atomicAdd(&feedback[${VGEO_FEEDBACK_OFFSET.STATS}u + slot], amount);
242
- }
243
-
244
- /**
245
- * Claim a page for one instance, and touch it if the claim was won.
246
- *
247
- * The claim is what makes "at most one draw record per (instance, cluster)" true, so an instance
248
- * whose slice of the bitmask does not fit is refused upstream rather than run unclaimed.
249
- */
250
- fn vg_claim_page(instance_base: u32, page_row: u32) -> bool {
251
- let ordinal = vg_page_field(page_row, ${VGEO_PAGE_ROW_OFFSET.ORDINAL}u);
252
-
253
- if (ordinal >= instances[instance_base + ${VGEO_INSTANCE_OFFSET.ORDINAL_CAPACITY}u]) {
254
- return false;
255
- }
256
-
257
- let bit = instances[instance_base + ${VGEO_INSTANCE_OFFSET.CLAIM_BASE}u] + ordinal;
258
-
259
- let word = bit >> 5u;
260
-
261
- if (word >= params.claim_word_count) {
262
- return false;
263
- }
264
-
265
- let mask = 1u << (bit & 31u);
266
-
267
- if ((atomicOr(&claim[word], mask) & mask) != 0u) {
268
- return false;
269
- }
270
-
271
- vg_stat(${VGEO_CUT_STAT.PAGES}u, 1u);
272
-
273
- vg_touch_page(page_row);
274
-
275
- return true;
276
- }
277
-
278
- /**
279
- * Record that this page was selected from this frame — design §6's eviction key.
280
- *
281
- * LRU on last-*touched*, never on last-wanted: stamping at fetch time freezes the stamp of exactly
282
- * the pages that are on screen and evicts them, which is the classic fallback-chain bug the virtual
283
- * texture cache already keys on touch to avoid.
284
- *
285
- * One bit per page row, because the feedback buffer is per frame rather than per invocation: the
286
- * bit dedups across every view and bucket of the frame and is the record the host reads, so
287
- * there is no list to fill and no touch a full list could lose.
288
- */
289
- fn vg_touch_page(page_row: u32) {
290
- atomicOr(&feedback[params.touch_bits_base + (page_row >> 5u)], 1u << (page_row & 31u));
291
- }
241
+ /*
242
+ vg_stat, vg_claim_page and vg_touch_page are chunk_vgeo_cut_claim's — the seed and reseed
243
+ passes take the same three, which is why they are no longer written here.
244
+ */
292
245
 
293
246
  /**
294
247
  * Ask for the page a clamped cluster could not reach.
@@ -906,6 +859,7 @@ fn main(
906
859
  }
907
860
  `, [
908
861
  chunk_vgeo_cut_access,
862
+ chunk_vgeo_cut_claim,
909
863
  MESHLET_DEFINITION_STRUCT.declaration_chunk,
910
864
  ]);
911
865
 
@@ -1 +1 @@
1
- {"version":3,"file":"load_gltf.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/loader/gltf/load_gltf.js"],"names":[],"mappings":"AAqIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,sDATW,YAAY,aAGZ,MAAM,iBACN,MAAM,kBACN;IAAC,CAAC,EAAC,MAAM,CAAC;IAAC,CAAC,EAAC,MAAM,CAAC;IAAC,CAAC,EAAC,MAAM,CAAC;IAAC,CAAC,EAAC,MAAM,CAAA;CAAC,QAgDlD;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,+BAdW,MAAM;IAEgB,YAAY;IAGN,KAAK;IACY,OAAO,GAApD,IAAI,MAAM,EAAE,IAAI,GAAC,IAAI,GAAC,WAAW,CAAC;IAGF,eAAe,GAA/C,qBAAqB;IAGpB,QAAQ,WAAW,CAAC,CA+0B/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wCARW,MAAM;IAEgB,YAAY;IACN,KAAK;IACY,OAAO,GAApD,IAAI,MAAM,EAAE,IAAI,GAAC,IAAI,GAAC,WAAW,CAAC;IACF,eAAe,GAA/C,qBAAqB;IACnB,QAAQ,QAAQ,CAAC,CAU7B;4BAtlC2B,mBAAmB;yBAUtB,gBAAgB"}
1
+ {"version":3,"file":"load_gltf.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/loader/gltf/load_gltf.js"],"names":[],"mappings":"AAmIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,sDATW,YAAY,aAGZ,MAAM,iBACN,MAAM,kBACN;IAAC,CAAC,EAAC,MAAM,CAAC;IAAC,CAAC,EAAC,MAAM,CAAC;IAAC,CAAC,EAAC,MAAM,CAAC;IAAC,CAAC,EAAC,MAAM,CAAA;CAAC,QAgDlD;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,+BAdW,MAAM;IAEgB,YAAY;IAGN,KAAK;IACY,OAAO,GAApD,IAAI,MAAM,EAAE,IAAI,GAAC,IAAI,GAAC,WAAW,CAAC;IAGF,eAAe,GAA/C,qBAAqB;IAGpB,QAAQ,WAAW,CAAC,CA8zB/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wCARW,MAAM;IAEgB,YAAY;IACN,KAAK;IACY,OAAO,GAApD,IAAI,MAAM,EAAE,IAAI,GAAC,IAAI,GAAC,WAAW,CAAC;IACF,eAAe,GAA/C,qBAAqB;IACnB,QAAQ,QAAQ,CAAC,CAU7B;4BApkC2B,mBAAmB;yBAStB,gBAAgB"}
@@ -9,7 +9,6 @@ import { clamp01 } from "../../../../core/math/clamp01.js";
9
9
  import { computeObjectHash } from "../../../../core/math/hash/computeObjectHash.js";
10
10
  import { inverseLerp } from "../../../../core/math/inverseLerp.js";
11
11
  import { objectDeepEquals } from "../../../../core/model/object/objectDeepEquals.js";
12
- import { number_format_by_thousands } from "../../../../core/primitives/numbers/number_format_by_thousands.js";
13
12
  import { AnimationInterpolationKind } from "../../../../engine/animation/clip/AnimationInterpolationKind.js";
14
13
  import { curve_from_track_data } from "../../../../engine/animation/clip/curve_from_track_data.js";
15
14
  import { AnimationCurve } from "../../../../engine/animation/curve/AnimationCurve.js";
@@ -35,7 +34,6 @@ import { ShadeImage } from "../../texture/source/ShadeImage.js";
35
34
  import { SceneBundle } from "../SceneBundle.js";
36
35
  import { SceneNode } from "../SceneNode.js";
37
36
  import { coalesce_array_duplicates } from "../../../../format/scene/gltf/coalesce_array_duplicates.js";
38
- import { fix_up_material_sides } from "./fix_up_material_sides.js";
39
37
  import { GLTF_COMPONENT_TYPE } from "../../../../format/scene/gltf/gltf_component_type.js";
40
38
  import { gltf_create_material } from "./gltf_create_material.js";
41
39
  import { gltf_create_texture } from "./gltf_create_texture.js";
@@ -1092,25 +1090,8 @@ export function load_gltf(url, { assetManager, scope, fileMap, texture_support }
1092
1090
  return bundle;
1093
1091
  }
1094
1092
 
1095
- /**
1096
- * @param {SceneBundle} bundle
1097
- */
1098
- function fix_scene_material_sidedness(bundle) {
1099
- const meshes = bundle.nodes.filter(node => node.geometry !== null);
1100
-
1101
- const { geometries_modified: fixed_count, primitives_delta } = fix_up_material_sides(meshes);
1102
-
1103
- if (fixed_count > 0) {
1104
- console.warn(`Fixed ${fixed_count} geometries in accordance with material sidedness. Primitives added: ${number_format_by_thousands(primitives_delta)}`);
1105
- }
1106
-
1107
- return bundle;
1108
- }
1109
-
1110
-
1111
1093
  return load_gltf_document(url, { assetManager, scope, fileMap, texture_support })
1112
1094
  .then(process)
1113
- // .then(fix_scene_material_sidedness)
1114
1095
  // .then(prepare_lightmap)
1115
1096
  ;
1116
1097
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * What a {@link ShadeDrawSide} means once `Back` stopped being a pipeline state of its own.
3
+ *
4
+ * A back-sided material is a front-sided one drawn with its winding reversed: the vertex shader
5
+ * emits each triangle's corners in the opposite order and the fixed-function rasterizer, still
6
+ * culling back faces, keeps the surface's back. That is what lets both share a bucket — see
7
+ * `chunk_rasterization_material_bucket` — and it is the same mechanism a reflected instance uses,
8
+ * so the two compose by exclusive-or.
9
+ *
10
+ * @type {CodeChunk}
11
+ */
12
+ export const chunk_material_draw_side: CodeChunk;
13
+ import { CodeChunk } from "../shader/compiler/CodeChunk.js";
14
+ //# sourceMappingURL=chunk_material_draw_side.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk_material_draw_side.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/material/chunk_material_draw_side.js"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,uCAFU,SAAS,CAqBb;0BAjCoB,iCAAiC"}
@@ -0,0 +1,34 @@
1
+ import { CodeChunk } from "../shader/compiler/CodeChunk.js";
2
+ import { ShadeDrawSide } from "./ShadeDrawSide.js";
3
+
4
+ /**
5
+ * What a {@link ShadeDrawSide} means once `Back` stopped being a pipeline state of its own.
6
+ *
7
+ * A back-sided material is a front-sided one drawn with its winding reversed: the vertex shader
8
+ * emits each triangle's corners in the opposite order and the fixed-function rasterizer, still
9
+ * culling back faces, keeps the surface's back. That is what lets both share a bucket — see
10
+ * `chunk_rasterization_material_bucket` — and it is the same mechanism a reflected instance uses,
11
+ * so the two compose by exclusive-or.
12
+ *
13
+ * @type {CodeChunk}
14
+ */
15
+ export const chunk_material_draw_side = CodeChunk.from(
16
+ //language=WGSL
17
+ `
18
+ /**
19
+ * Whether the material asks for the back face, and therefore for reversed winding.
20
+ */
21
+ fn material_draws_back_side( draw_side: u32 ) -> bool{
22
+ return draw_side == ${ShadeDrawSide.Back}u;
23
+ }
24
+
25
+ /**
26
+ * Whether the fragment is looking at the front of the surface.
27
+ *
28
+ * Not \`front_facing\`: that is the rasterizer's answer about the winding the vertex shader
29
+ * emitted, and a back-sided material emitted it reversed on purpose.
30
+ */
31
+ fn material_fragment_faces_front( front_facing: bool, draw_side: u32 ) -> bool{
32
+ return front_facing != material_draws_back_side(draw_side);
33
+ }
34
+ `, [])
@@ -1 +1 @@
1
- {"version":3,"file":"fragment_gbuffer.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/material/standard/fragment_gbuffer.js"],"names":[],"mappings":"AA6EA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IAR6B,OAAO,GAAzB,MAAM;IACW,GAAG,EAApB,MAAM;IACW,MAAM,EAAvB,MAAM;IACW,OAAO,EAAxB,MAAM;IACW,QAAQ,EAAzB,MAAM;kBACN,SAAS,EAAE,GACT,SAAS,CAkOrB;AAjRD,oEAA2E;AAI3E,oIAEG;AASH,sIAEG;AAyQH,8CAIE;0BA1SwB,oCAAoC;4CAClB,sDAAsD;iCACjE,kCAAkC"}
1
+ {"version":3,"file":"fragment_gbuffer.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/material/standard/fragment_gbuffer.js"],"names":[],"mappings":"AA8EA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH;IAR6B,OAAO,GAAzB,MAAM;IACW,GAAG,EAApB,MAAM;IACW,MAAM,EAAvB,MAAM;IACW,OAAO,EAAxB,MAAM;IACW,QAAQ,EAAzB,MAAM;kBACN,SAAS,EAAE,GACT,SAAS,CAwOrB;AAvRD,oEAA2E;AAI3E,oIAEG;AASH,sIAEG;AA+QH,8CAIE;0BAhTwB,oCAAoC;4CAClB,sDAAsD;iCACjE,kCAAkC"}
@@ -6,6 +6,7 @@ import { CAMERA_UNIFORM_STRUCT } from "../../camera/CAMERA_UNIFORM_STRUCT.js";
6
6
  import { GEOMETRY_ATTRIBUTE_DATA_TYPE } from "../../geometry/GEOMETRY_ATTRIBUTE_DATA_TYPE.js";
7
7
  import { GEOMETRY_METADATA_STRUCT } from "../../geometry/GEOMETRY_METADATA_STRUCT.js";
8
8
  import { MESHLET_METADATA_STRUCT } from "../../geometry/meshlet/encoding/MESHLET_METADATA_STRUCT.js";
9
+ import { chunk_node3d_flags } from "../../scene/chunk_node3d_flags.js";
9
10
  import { chunk_scene_database_access } from "../../scene/chunk_scene_database_access.js";
10
11
  import { RENDER_GLOBAL_MIP_BIAS } from "../../postprocess/taa/RENDER_GLOBAL_MIP_BIAS.js";
11
12
  import { chunk_barycentric_full } from "../../shader/chunk/geometry/triangle/chunk_barycentric_full.js";
@@ -225,7 +226,12 @@ fn main(
225
226
  );
226
227
 
227
228
  // Get tangent handedness from the provoking vertex (0).
228
- let geometry_tangent_handedness = vertex_0.tangent.w;
229
+ // a reflection changes the tangent frame's handedness — see NODE3D_GLOBAL_FLAG_MIRRORED
230
+ let geometry_tangent_handedness = select(
231
+ vertex_0.tangent.w,
232
+ -vertex_0.tangent.w,
233
+ node3d_global_is_mirrored(node)
234
+ );
229
235
 
230
236
  let geometry_color = interpolate_attribute_3f32(
231
237
  vertex_0.color,
@@ -316,6 +322,7 @@ fn main(
316
322
  chunk_rgbe9995_encode,
317
323
  chunk_dither_color_8bit_triangle_noise,
318
324
  chunk_scene_database_access,
325
+ chunk_node3d_flags,
319
326
 
320
327
  ...extra_chunks,
321
328
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"chunk_sample_material_data.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js"],"names":[],"mappings":"AA8BA;;;GAGG;AACH,0CAFU,SAAS,CA8FlB;AAED,mDAsHC;0BA/NyB,oCAAoC"}
1
+ {"version":3,"file":"chunk_sample_material_data.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/path_tracer/shader/chunk_sample_material_data.js"],"names":[],"mappings":"AAiCA;;;GAGG;AACH,0CAFU,SAAS,CA8FlB;AAED,mDA+HC;0BAxOyB,oCAAoC"}
@@ -1,4 +1,7 @@
1
1
  import { chunk_compute_normal_matrix_from_m4 } from "../../shader/chunk/geometry/mat4/chunk_compute_normal_matrix_from_m4.js";
2
+ import {
3
+ chunk_mat4_linear_determinant
4
+ } from "../../shader/chunk/geometry/mat4/chunk_mat4_linear_determinant.js";
2
5
  import { chunk_decode_tangent_normal } from "../../shader/chunk/geometry/normal/chunk_decode_tangent_normal.js";
3
6
  import { chunk_F_Hauber } from "../../shader/chunk/brdf/fresnel/chunk_F_Hauber.js";
4
7
  import {
@@ -201,7 +204,15 @@ fn sample_material_data(
201
204
  world_face_normal = -world_face_normal;
202
205
  }
203
206
 
204
- let world_tangent_frame = build_orthonormal_matrix_nt(world_vertex_normal, vec4(world_tangent, geometry_tangent.w));
207
+ // a reflection changes the tangent frame's handedness — see NODE3D_GLOBAL_FLAG_MIRRORED.
208
+ // No node here: the tracer is handed a matrix, so the sign comes off the matrix.
209
+ let tangent_handedness = select(
210
+ geometry_tangent.w,
211
+ -geometry_tangent.w,
212
+ mat4_linear_determinant(instance_transform) < 0.0
213
+ );
214
+
215
+ let world_tangent_frame = build_orthonormal_matrix_nt(world_vertex_normal, vec4(world_tangent, tangent_handedness));
205
216
 
206
217
  var normal_sample = decode_tangent_normal(indirect_sample_texture(material.texture_normal, material.sampler_normal, uv));
207
218
 
@@ -238,6 +249,7 @@ fn sample_material_data(
238
249
  chunk_interpolate_attribute_3f32,
239
250
  chunk_interpolate_attribute_4f32,
240
251
  chunk_compute_normal_matrix_from_m4,
252
+ chunk_mat4_linear_determinant,
241
253
  chunk_build_orthonormal_matrix_nt,
242
254
  chunk_decode_tangent_normal,
243
255
  chunk_compute_triangle_face_normal,
@@ -9,9 +9,14 @@ export function construct_primitive_state(draw_mode: ShadeDrawMode, side: ShadeD
9
9
  /**
10
10
  * Named cull-mode lookup tables, keyed by {@link ShadeDrawSide }.
11
11
  *
12
- * - `Standard` is the main-pass mapping: a `Front`-sided material renders its front face
13
- * (cull back), `Back` renders its back face (cull front), `Double` renders both.
14
- * - `Reversed` flips sided materials so the *back* face is what gets drawn. This is the
12
+ * `Front` and `Back` map to the same cull mode, because the winding they differ by is the
13
+ * vertex shader's to reverse — see `chunk_triangle_list_reverse_winding`. That is what lets
14
+ * them share a bucket, and it is why nothing here needs a fourth entry for the *instance's*
15
+ * handedness either.
16
+ *
17
+ * - `Standard` is the main-pass mapping: a one-sided material renders the face its winding
18
+ * presents (cull back), `Double` renders both.
19
+ * - `Reversed` culls the other way, so the *back* face is what gets drawn. This is the
15
20
  * standard "back-face shadow casting" / "front-face culling for shadow casters"
16
21
  * technique — shadow callers select this policy so the surface written into the shadow
17
22
  * map sits behind the lit surface, eliminating self-shadow acne on the lit side and
@@ -21,12 +26,21 @@ export function construct_primitive_state(draw_mode: ShadeDrawMode, side: ShadeD
21
26
  export type CullPolicy = Readonly<Record<number, GPUCullMode>>;
22
27
  export namespace CullPolicy {
23
28
  let Standard: Readonly<{
24
- [x: number]: "none" | "back" | "front";
29
+ [x: number]: "none" | "back";
25
30
  }>;
26
31
  let Reversed: Readonly<{
27
- [x: number]: "none" | "back" | "front";
32
+ [x: number]: "none" | "front";
28
33
  }>;
29
34
  }
35
+ /**
36
+ * One {@link ShadeDrawSide} per rasterization bucket — the sides that still differ once `Back`
37
+ * is drawn as a reversed `Front`. Callers that enumerate buckets iterate this rather than the
38
+ * enum, so a `Back`-sided material is picked up by the `Front` pass rather than by a pass of
39
+ * its own that would repeat the whole expansion chain for it.
40
+ *
41
+ * @type {ReadonlyArray<ShadeDrawSide>}
42
+ */
43
+ export const RASTERIZATION_BUCKET_SIDES: ReadonlyArray<ShadeDrawSide>;
30
44
  import { ShadeDrawMode } from "../../material/ShadeDrawMode.js";
31
45
  import { ShadeDrawSide } from "../../material/ShadeDrawSide.js";
32
46
  import { PrimitiveState } from "../../../descriptor/pipeline/render/PrimitiveState.js";
@@ -1 +1 @@
1
- {"version":3,"file":"construct_primitive_state.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/construct_primitive_state.js"],"names":[],"mappings":"AAoCA;;;;;;GAMG;AACH,qDALW,aAAa,QACb,aAAa,gBACb,SAAS,OAAO,MAAM,cAAc,CAAC,GACnC,cAAc,CAgB1B;;;;;;;;;;;;;yBApCS,SAAS,OAAO,MAAM,cAAc,CAAC;;;;;;;;;8BApBjB,iCAAiC;8BACjC,iCAAiC;+BAFhC,uDAAuD"}
1
+ {"version":3,"file":"construct_primitive_state.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/construct_primitive_state.js"],"names":[],"mappings":"AAsDA;;;;;;GAMG;AACH,qDALW,aAAa,QACb,aAAa,gBACb,SAAS,OAAO,MAAM,cAAc,CAAC,GACnC,cAAc,CAgB1B;;;;;;;;;;;;;;;;;;yBAjDS,SAAS,OAAO,MAAM,cAAc,CAAC;;;;;;;;;AAe/C;;;;;;;GAOG;AACH,yCAFU,cAAc,aAAa,CAAC,CAKnC;8BAnD2B,iCAAiC;8BACjC,iCAAiC;+BAFhC,uDAAuD"}
@@ -10,9 +10,14 @@ const lut_topology = {
10
10
  /**
11
11
  * Named cull-mode lookup tables, keyed by {@link ShadeDrawSide}.
12
12
  *
13
- * - `Standard` is the main-pass mapping: a `Front`-sided material renders its front face
14
- * (cull back), `Back` renders its back face (cull front), `Double` renders both.
15
- * - `Reversed` flips sided materials so the *back* face is what gets drawn. This is the
13
+ * `Front` and `Back` map to the same cull mode, because the winding they differ by is the
14
+ * vertex shader's to reverse — see `chunk_triangle_list_reverse_winding`. That is what lets
15
+ * them share a bucket, and it is why nothing here needs a fourth entry for the *instance's*
16
+ * handedness either.
17
+ *
18
+ * - `Standard` is the main-pass mapping: a one-sided material renders the face its winding
19
+ * presents (cull back), `Double` renders both.
20
+ * - `Reversed` culls the other way, so the *back* face is what gets drawn. This is the
16
21
  * standard "back-face shadow casting" / "front-face culling for shadow casters"
17
22
  * technique — shadow callers select this policy so the surface written into the shadow
18
23
  * map sits behind the lit surface, eliminating self-shadow acne on the lit side and
@@ -24,16 +29,29 @@ const lut_topology = {
24
29
  export const CullPolicy = {
25
30
  Standard: Object.freeze({
26
31
  [ShadeDrawSide.Front]: 'back',
27
- [ShadeDrawSide.Back]: 'front',
32
+ [ShadeDrawSide.Back]: 'back',
28
33
  [ShadeDrawSide.Double]: 'none',
29
34
  }),
30
35
  Reversed: Object.freeze({
31
36
  [ShadeDrawSide.Front]: 'front',
32
- [ShadeDrawSide.Back]: 'back',
37
+ [ShadeDrawSide.Back]: 'front',
33
38
  [ShadeDrawSide.Double]: 'none',
34
39
  }),
35
40
  };
36
41
 
42
+ /**
43
+ * One {@link ShadeDrawSide} per rasterization bucket — the sides that still differ once `Back`
44
+ * is drawn as a reversed `Front`. Callers that enumerate buckets iterate this rather than the
45
+ * enum, so a `Back`-sided material is picked up by the `Front` pass rather than by a pass of
46
+ * its own that would repeat the whole expansion chain for it.
47
+ *
48
+ * @type {ReadonlyArray<ShadeDrawSide>}
49
+ */
50
+ export const RASTERIZATION_BUCKET_SIDES = Object.freeze([
51
+ ShadeDrawSide.Front,
52
+ ShadeDrawSide.Double,
53
+ ]);
54
+
37
55
  /**
38
56
  *
39
57
  * @param {ShadeDrawMode} draw_mode
@@ -1 +1 @@
1
- {"version":3,"file":"filter_materials_by_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js"],"names":[],"mappings":"AAKA;;;;;;;;;;GAUG;AACH,mDATW,eAAe,iBACf,MAAM,cACN,gBAAgB,aAChB,aAAa,aACb,aAAa,aACb,eAAe,SACf,MAAM,GACJ,MAAM,CAsClB;iCAjDgC,oCAAoC;8BAFvC,iCAAiC;8BACjC,iCAAiC"}
1
+ {"version":3,"file":"filter_materials_by_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/filter_materials_by_bucket.js"],"names":[],"mappings":"AAMA;;;;;;;;;;GAUG;AACH,mDATW,eAAe,iBACf,MAAM,cACN,gBAAgB,aAChB,aAAa,aACb,aAAa,aACb,eAAe,SACf,MAAM,GACJ,MAAM,CA4ClB;iCAxDgC,oCAAoC;8BAFvC,iCAAiC;8BACjC,iCAAiC"}
@@ -2,6 +2,7 @@ import { assert } from "../../../../core/assert.js";
2
2
  import { ShadeDrawMode } from "../../material/ShadeDrawMode.js";
3
3
  import { ShadeDrawSide } from "../../material/ShadeDrawSide.js";
4
4
  import { TransparencyMode } from "../../material/TransparencyMode.js";
5
+ import { rasterization_material_bucket } from "../expand/bucket/chunk_rasterization_material_bucket.js";
5
6
 
6
7
  /**
7
8
  *
@@ -33,14 +34,20 @@ export function filter_materials_by_bucket(
33
34
 
34
35
  let material_count = 0;
35
36
 
37
+ // by bucket, not field by field: `Front` and `Back` are one bucket, so asking for either
38
+ // yields both, which is what puts them in a single pass
39
+ const bucket = rasterization_material_bucket(alpha_mode, draw_mode, draw_side);
40
+
36
41
  for (let i = 0; i < count; i++) {
37
42
  const material = materials[i];
38
43
 
39
- if (
40
- material.transparency_mode !== alpha_mode
41
- || material.draw_mode !== draw_mode
42
- || material.draw_side !== draw_side
43
- ) {
44
+ const material_bucket = rasterization_material_bucket(
45
+ material.transparency_mode,
46
+ material.draw_mode,
47
+ material.draw_side
48
+ );
49
+
50
+ if (material_bucket !== bucket) {
44
51
  continue;
45
52
  }
46
53