@woosh/meep-engine 3.69.0 → 3.70.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 (398) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/build/bundle-worker-terrain.js +1 -1
  3. package/package.json +1 -1
  4. package/src/core/bvh8/build/BVH8Converter.d.ts +16 -0
  5. package/src/core/bvh8/build/BVH8Converter.d.ts.map +1 -1
  6. package/src/core/bvh8/build/BVH8Converter.js +64 -37
  7. package/src/core/bvh8/build/bvh8_assign_slots_greedy.d.ts +25 -0
  8. package/src/core/bvh8/build/bvh8_assign_slots_greedy.d.ts.map +1 -0
  9. package/src/core/bvh8/build/bvh8_assign_slots_greedy.js +68 -0
  10. package/src/core/bvh8/build/bvh8_from_proxy.d.ts.map +1 -1
  11. package/src/core/bvh8/build/bvh8_from_proxy.js +2 -33
  12. package/src/core/bvh8/build/bvh8_node_fit_slots.d.ts +22 -0
  13. package/src/core/bvh8/build/bvh8_node_fit_slots.d.ts.map +1 -0
  14. package/src/core/bvh8/build/bvh8_node_fit_slots.js +115 -0
  15. package/src/core/bvh8/bvh8_depth.d.ts +8 -0
  16. package/src/core/bvh8/bvh8_depth.d.ts.map +1 -0
  17. package/src/core/bvh8/bvh8_depth.js +39 -0
  18. package/src/core/math/complex/complex_fft_radix2.d.ts +19 -0
  19. package/src/core/math/complex/complex_fft_radix2.d.ts.map +1 -0
  20. package/src/core/math/complex/complex_fft_radix2.js +71 -0
  21. package/src/engine/graphics3/GPUFluidSystem.d.ts +17 -4
  22. package/src/engine/graphics3/GPUFluidSystem.d.ts.map +1 -1
  23. package/src/engine/graphics3/GPUFluidSystem.js +113 -16
  24. package/src/engine/physics/cloth/playground/wind_main.js +7 -6
  25. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +2 -2
  26. package/src/engine/physics/cloth/wind/AbstractClothWind.js +2 -2
  27. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts +3 -3
  28. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -1
  29. package/src/engine/physics/cloth/wind/ClothFluidWind.js +7 -9
  30. package/src/engine/physics/cloth/wind/ClothGPUFluidWind.d.ts +10 -10
  31. package/src/engine/physics/cloth/wind/ClothGPUFluidWind.d.ts.map +1 -1
  32. package/src/engine/physics/cloth/wind/ClothGPUFluidWind.js +14 -22
  33. package/src/engine/physics/fluid/FluidAtmosphere.d.ts +16 -2
  34. package/src/engine/physics/fluid/FluidAtmosphere.d.ts.map +1 -1
  35. package/src/engine/physics/fluid/FluidAtmosphere.js +17 -3
  36. package/src/engine/physics/fluid/ecs/FluidEffectorsComponentSerializationAdapter.d.ts +1 -2
  37. package/src/engine/physics/fluid/ecs/FluidEffectorsComponentSerializationAdapter.d.ts.map +1 -1
  38. package/src/engine/physics/fluid/ecs/FluidEffectorsComponentSerializationAdapter.js +1 -2
  39. package/src/engine/physics/fluid/ecs/FluidEffectorsSystem.d.ts +8 -5
  40. package/src/engine/physics/fluid/ecs/FluidEffectorsSystem.d.ts.map +1 -1
  41. package/src/engine/physics/fluid/ecs/FluidEffectorsSystem.js +60 -17
  42. package/src/engine/physics/fluid/effector/AbstractFluidEffector.d.ts +1 -1
  43. package/src/engine/physics/fluid/effector/AbstractFluidEffector.js +1 -1
  44. package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts +33 -47
  45. package/src/engine/physics/fluid/effector/GlobalFluidEffector.d.ts.map +1 -1
  46. package/src/engine/physics/fluid/effector/GlobalFluidEffector.js +38 -176
  47. package/src/engine/physics/fluid/effector/GlobalFluidEffectorSerializationAdapter.d.ts +8 -11
  48. package/src/engine/physics/fluid/effector/GlobalFluidEffectorSerializationAdapter.d.ts.map +1 -1
  49. package/src/engine/physics/fluid/effector/GlobalFluidEffectorSerializationAdapter.js +13 -24
  50. package/src/engine/physics/fluid/effector/GlobalFluidEffectorSerializationUpgrader_0_1.d.ts +16 -0
  51. package/src/engine/physics/fluid/effector/GlobalFluidEffectorSerializationUpgrader_0_1.d.ts.map +1 -0
  52. package/src/engine/physics/fluid/effector/GlobalFluidEffectorSerializationUpgrader_0_1.js +35 -0
  53. package/src/engine/physics/fluid/fluid_apply_atmosphere.d.ts +1 -1
  54. package/src/engine/physics/fluid/fluid_apply_atmosphere.js +1 -1
  55. package/src/engine/physics/fluid/turbulence/FLUID_TURBULENCE_TILE.d.ts +28 -0
  56. package/src/engine/physics/fluid/turbulence/FLUID_TURBULENCE_TILE.d.ts.map +1 -0
  57. package/src/engine/physics/fluid/turbulence/FLUID_TURBULENCE_TILE.js +29 -0
  58. package/src/engine/physics/fluid/turbulence/fluid_atmosphere_velocity_at.d.ts +16 -0
  59. package/src/engine/physics/fluid/turbulence/fluid_atmosphere_velocity_at.d.ts.map +1 -0
  60. package/src/engine/physics/fluid/turbulence/fluid_atmosphere_velocity_at.js +49 -0
  61. package/src/engine/physics/fluid/turbulence/fluid_turbulence_inverse_tile_size.d.ts +11 -0
  62. package/src/engine/physics/fluid/turbulence/fluid_turbulence_inverse_tile_size.d.ts.map +1 -0
  63. package/src/engine/physics/fluid/turbulence/fluid_turbulence_inverse_tile_size.js +18 -0
  64. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_data.d.ts +25 -0
  65. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_data.d.ts.map +1 -0
  66. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_data.js +65 -0
  67. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_generate.d.ts +56 -0
  68. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_generate.d.ts.map +1 -0
  69. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_generate.js +313 -0
  70. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_sample.d.ts +16 -0
  71. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_sample.d.ts.map +1 -0
  72. package/src/engine/physics/fluid/turbulence/fluid_turbulence_tile_sample.js +69 -0
  73. package/src/generation/foliage/mesh/branch_ring_triangulate.d.ts +1 -1
  74. package/src/generation/foliage/mesh/branch_ring_triangulate.js +1 -1
  75. package/src/shade/playground/bvh_repro/README.md +8 -6
  76. package/src/shade/playground/bvh_repro/index.html +125 -124
  77. package/src/shade/playground/bvh_repro/main.js +259 -254
  78. package/src/shade/playground/foliage_wind/foliage_world.d.ts.map +1 -1
  79. package/src/shade/playground/foliage_wind/foliage_world.js +7 -4
  80. package/src/shade/playground/grass_surfaces/main.js +3 -2
  81. package/src/shade/playground/particle_ecs/bonfire_wind.d.ts +23 -13
  82. package/src/shade/playground/particle_ecs/bonfire_wind.d.ts.map +1 -1
  83. package/src/shade/playground/particle_ecs/bonfire_wind.js +30 -33
  84. package/src/shade/playground/skinned_blas_refit/README.md +26 -14
  85. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts +14 -9
  86. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.d.ts.map +1 -1
  87. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_leaf_triangles.js +126 -127
  88. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_points.js +95 -95
  89. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts +9 -1
  90. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.d.ts.map +1 -1
  91. package/src/shade/playground/skinned_blas_refit/shader_blas_audit_visibility.js +146 -135
  92. package/src/shade/playground/skinned_blas_refit/verify_clone_leaves.d.ts +6 -6
  93. package/src/shade/playground/skinned_blas_refit/verify_clone_leaves.d.ts.map +1 -1
  94. package/src/shade/playground/skinned_blas_refit/verify_clone_leaves.js +148 -134
  95. package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.d.ts +11 -8
  96. package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.d.ts.map +1 -1
  97. package/src/shade/playground/skinned_blas_refit/verify_leaf_triangles.js +342 -202
  98. package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts +12 -13
  99. package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.d.ts.map +1 -1
  100. package/src/shade/playground/skinned_blas_refit/verify_skinned_blas_refit.js +136 -116
  101. package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.d.ts +4 -4
  102. package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.d.ts.map +1 -1
  103. package/src/shade/playground/skinned_blas_refit/verify_traversal_reachability.js +255 -249
  104. package/src/shade/playground/water_surface/main.js +6 -3
  105. package/src/shade/renderer/animation/GPUAnimationManager.js +2 -2
  106. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  107. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +7 -8
  108. package/src/shade/renderer/fluid/FLUID_FAR_FIELD_STRUCT.d.ts +111 -0
  109. package/src/shade/renderer/fluid/FLUID_FAR_FIELD_STRUCT.d.ts.map +1 -0
  110. package/src/shade/renderer/fluid/FLUID_FAR_FIELD_STRUCT.js +162 -0
  111. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +37 -8
  112. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts.map +1 -1
  113. package/src/shade/renderer/fluid/GPUFluidCascade.js +149 -47
  114. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +10 -3
  115. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts.map +1 -1
  116. package/src/shade/renderer/fluid/GPUFluidCascadeStack.js +69 -10
  117. package/src/shade/renderer/fluid/chunk_fluid_far_field.d.ts +19 -0
  118. package/src/shade/renderer/fluid/chunk_fluid_far_field.d.ts.map +1 -0
  119. package/src/shade/renderer/fluid/chunk_fluid_far_field.js +43 -0
  120. package/src/shade/renderer/fluid/coupling/FLUID_CASCADE_PARENT_STRUCT.d.ts +10 -9
  121. package/src/shade/renderer/fluid/coupling/FLUID_CASCADE_PARENT_STRUCT.d.ts.map +1 -1
  122. package/src/shade/renderer/fluid/coupling/FLUID_CASCADE_PARENT_STRUCT.js +14 -12
  123. package/src/shade/renderer/fluid/coupling/FLUID_CASCADE_RESTRICT_STRUCT.d.ts +3 -2
  124. package/src/shade/renderer/fluid/coupling/FLUID_CASCADE_RESTRICT_STRUCT.d.ts.map +1 -1
  125. package/src/shade/renderer/fluid/coupling/FLUID_CASCADE_RESTRICT_STRUCT.js +7 -5
  126. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_epilogue.d.ts +17 -9
  127. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_epilogue.d.ts.map +1 -1
  128. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_epilogue.js +58 -18
  129. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.d.ts +23 -11
  130. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.d.ts.map +1 -1
  131. package/src/shade/renderer/fluid/coupling/chunk_fluid_cascade_parent.js +371 -335
  132. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_fill.d.ts.map +1 -1
  133. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_fill.js +2 -1
  134. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_ghosts.d.ts.map +1 -1
  135. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_ghosts.js +109 -108
  136. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_restrict.d.ts +5 -3
  137. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_restrict.d.ts.map +1 -1
  138. package/src/shade/renderer/fluid/coupling/shader_fluid_cascade_restrict.js +24 -12
  139. package/src/shade/renderer/fluid/mac/FLUID_MAC_STEP_STRUCT.d.ts +6 -2
  140. package/src/shade/renderer/fluid/mac/FLUID_MAC_STEP_STRUCT.d.ts.map +1 -1
  141. package/src/shade/renderer/fluid/mac/FLUID_MAC_STEP_STRUCT.js +10 -5
  142. package/src/shade/renderer/fluid/mac/FluidMacPasses.d.ts +20 -21
  143. package/src/shade/renderer/fluid/mac/FluidMacPasses.d.ts.map +1 -1
  144. package/src/shade/renderer/fluid/mac/FluidMacPasses.js +453 -427
  145. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.d.ts.map +1 -1
  146. package/src/shade/renderer/fluid/mac/shader_fluid_mac_advect_correct.js +2 -1
  147. package/src/shade/renderer/fluid/publish/FluidWindPublish.d.ts +13 -5
  148. package/src/shade/renderer/fluid/publish/FluidWindPublish.d.ts.map +1 -1
  149. package/src/shade/renderer/fluid/publish/FluidWindPublish.js +20 -10
  150. package/src/shade/renderer/fluid/publish/chunk_fluid_wind_publish_cell.d.ts +2 -2
  151. package/src/shade/renderer/fluid/publish/chunk_fluid_wind_publish_cell.js +2 -2
  152. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.d.ts +5 -3
  153. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.d.ts.map +1 -1
  154. package/src/shade/renderer/fluid/publish/shader_fluid_wind_publish.js +147 -142
  155. package/src/shade/renderer/fluid/query/GPUFluidQueries.js +2 -2
  156. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  157. package/src/shade/renderer/geometry/GPUGeometryManager.js +5 -7
  158. package/src/shade/renderer/geometry/MeshletGeometry.d.ts +4 -1
  159. package/src/shade/renderer/geometry/MeshletGeometry.d.ts.map +1 -1
  160. package/src/shade/renderer/geometry/MeshletGeometry.js +4 -1
  161. package/src/shade/renderer/geometry/MeshletGeometrySerializationAdapter.d.ts +20 -0
  162. package/src/shade/renderer/geometry/MeshletGeometrySerializationAdapter.d.ts.map +1 -1
  163. package/src/shade/renderer/geometry/MeshletGeometrySerializationAdapter.js +35 -3
  164. package/src/shade/renderer/geometry/blas_from_meshlets.d.ts +9 -3
  165. package/src/shade/renderer/geometry/blas_from_meshlets.d.ts.map +1 -1
  166. package/src/shade/renderer/geometry/blas_from_meshlets.js +16 -30
  167. package/src/shade/renderer/geometry/bvh/BLAS8_REFIT_TARGET_STRUCT.d.ts +14 -0
  168. package/src/shade/renderer/geometry/bvh/BLAS8_REFIT_TARGET_STRUCT.d.ts.map +1 -0
  169. package/src/shade/renderer/geometry/bvh/BLAS8_REFIT_TARGET_STRUCT.js +33 -0
  170. package/src/shade/renderer/geometry/bvh/GPUBLASRefitBatch.d.ts +18 -21
  171. package/src/shade/renderer/geometry/bvh/GPUBLASRefitBatch.d.ts.map +1 -1
  172. package/src/shade/renderer/geometry/bvh/GPUBLASRefitBatch.js +78 -158
  173. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +18 -39
  174. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  175. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +39 -398
  176. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts +6 -7
  177. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts.map +1 -1
  178. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.js +5 -7
  179. package/src/shade/renderer/geometry/bvh/bvh8_build_for_meshlets.d.ts +36 -0
  180. package/src/shade/renderer/geometry/bvh/bvh8_build_for_meshlets.d.ts.map +1 -0
  181. package/src/shade/renderer/geometry/bvh/bvh8_build_for_meshlets.js +179 -0
  182. package/src/shade/renderer/geometry/bvh/meshlet_batch_to_clustered_bvh.d.ts +30 -0
  183. package/src/shade/renderer/geometry/bvh/meshlet_batch_to_clustered_bvh.d.ts.map +1 -0
  184. package/src/shade/renderer/geometry/bvh/meshlet_batch_to_clustered_bvh.js +207 -0
  185. package/src/shade/renderer/geometry/bvh/shader_blas8_refit_batch.d.ts +52 -0
  186. package/src/shade/renderer/geometry/bvh/shader_blas8_refit_batch.d.ts.map +1 -0
  187. package/src/shade/renderer/geometry/bvh/shader_blas8_refit_batch.js +464 -0
  188. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts +12 -0
  189. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  190. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +37 -0
  191. package/src/shade/renderer/geometry/sdf/bake/shader_mesh_sdf_classify.d.ts.map +1 -1
  192. package/src/shade/renderer/geometry/sdf/bake/shader_mesh_sdf_classify.js +4 -4
  193. package/src/shade/renderer/geometry/sdf/bake/shader_mesh_sdf_evaluate.d.ts.map +1 -1
  194. package/src/shade/renderer/geometry/sdf/bake/shader_mesh_sdf_evaluate.js +2 -2
  195. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.d.ts.map +1 -1
  196. package/src/shade/renderer/geometry/sdf/chunk_blas_query_band_sdf.js +2 -2
  197. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.d.ts.map +1 -1
  198. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.js +4 -4
  199. package/src/shade/renderer/geometry/sdf/surface/shader_mesh_surface_trace.d.ts.map +1 -1
  200. package/src/shade/renderer/geometry/sdf/surface/shader_mesh_surface_trace.js +2 -2
  201. package/src/shade/renderer/geometry/sr_create_group_RTX.d.ts.map +1 -1
  202. package/src/shade/renderer/geometry/sr_create_group_RTX.js +4 -4
  203. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
  204. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +1 -0
  205. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
  206. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +1 -0
  207. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.d.ts.map +1 -1
  208. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.js +372 -376
  209. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK.d.ts +9 -10
  210. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK.d.ts.map +1 -1
  211. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/MESH_BRICK.js +10 -11
  212. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.d.ts +2 -2
  213. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/chunk_mesh_brick_walk.js +2 -2
  214. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.d.ts +12 -8
  215. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.d.ts.map +1 -1
  216. package/src/shade/renderer/global_illumination/probe_clipmap/brick/mesh/shader_mesh_brick_gather.js +109 -60
  217. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_mesh_sdf.js +2 -2
  218. package/src/shade/renderer/global_illumination/probe_clipmap/debug/graph_probe_clipmap_debug_mesh_surfaces.js +2 -2
  219. package/src/shade/renderer/gpu_primitive/bvh/BVH_BUFFER_LAYOUT.d.ts +2 -2
  220. package/src/shade/renderer/gpu_primitive/bvh/BVH_BUFFER_LAYOUT.js +70 -70
  221. package/src/shade/renderer/gpu_primitive/bvh/BVH_BUILD_SETTINGS_STRUCT.d.ts +6 -7
  222. package/src/shade/renderer/gpu_primitive/bvh/BVH_BUILD_SETTINGS_STRUCT.d.ts.map +1 -1
  223. package/src/shade/renderer/gpu_primitive/bvh/BVH_BUILD_SETTINGS_STRUCT.js +6 -7
  224. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts +3 -3
  225. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +3 -3
  226. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_build_single_workgroup.js +1 -1
  227. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.d.ts +2 -2
  228. package/src/shade/renderer/gpu_primitive/bvh/shader_bvh_emit_hierarchy.js +3 -3
  229. package/src/shade/renderer/gpu_primitive/bvh8/BVH8_TREE_BUFFER_LAYOUT.d.ts +69 -0
  230. package/src/shade/renderer/gpu_primitive/bvh8/BVH8_TREE_BUFFER_LAYOUT.d.ts.map +1 -0
  231. package/src/shade/renderer/gpu_primitive/bvh8/BVH8_TREE_BUFFER_LAYOUT.js +89 -0
  232. package/src/shade/renderer/gpu_primitive/bvh8/bvh8_encode_from_bvh2.d.ts +41 -0
  233. package/src/shade/renderer/gpu_primitive/bvh8/bvh8_encode_from_bvh2.d.ts.map +1 -0
  234. package/src/shade/renderer/gpu_primitive/bvh8/bvh8_encode_from_bvh2.js +348 -0
  235. package/src/shade/renderer/gpu_primitive/bvh8/check_bvh8_tree.d.ts +34 -0
  236. package/src/shade/renderer/gpu_primitive/bvh8/check_bvh8_tree.d.ts.map +1 -0
  237. package/src/shade/renderer/gpu_primitive/bvh8/check_bvh8_tree.js +291 -0
  238. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_assign.d.ts +31 -0
  239. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_assign.d.ts.map +1 -0
  240. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_assign.js +175 -0
  241. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_depth.d.ts +27 -0
  242. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_depth.d.ts.map +1 -0
  243. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_depth.js +48 -0
  244. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_gather.d.ts +26 -0
  245. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_gather.d.ts.map +1 -0
  246. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_gather.js +118 -0
  247. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_node.d.ts +22 -0
  248. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_node.d.ts.map +1 -0
  249. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_node.js +143 -0
  250. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_sums.d.ts +36 -0
  251. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_sums.d.ts.map +1 -0
  252. package/src/shade/renderer/gpu_primitive/bvh8/chunk_bvh8_encode_sums.js +97 -0
  253. package/src/shade/renderer/gpu_primitive/bvh8/graph_bvh8_encode_from_bvh2.d.ts +49 -0
  254. package/src/shade/renderer/gpu_primitive/bvh8/graph_bvh8_encode_from_bvh2.d.ts.map +1 -0
  255. package/src/shade/renderer/gpu_primitive/bvh8/graph_bvh8_encode_from_bvh2.js +265 -0
  256. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_assign.d.ts +37 -0
  257. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_assign.d.ts.map +1 -0
  258. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_assign.js +109 -0
  259. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_count.d.ts +15 -0
  260. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_count.d.ts.map +1 -0
  261. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_count.js +85 -0
  262. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_count_single_workgroup.d.ts +26 -0
  263. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_count_single_workgroup.d.ts.map +1 -0
  264. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_count_single_workgroup.js +200 -0
  265. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_parents.d.ts +19 -0
  266. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_parents.d.ts.map +1 -0
  267. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_parents.js +81 -0
  268. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_write.d.ts +14 -0
  269. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_write.d.ts.map +1 -0
  270. package/src/shade/renderer/gpu_primitive/bvh8/shader_bvh8_encode_write.js +101 -0
  271. package/src/shade/renderer/gpu_primitive/bvh8/verify_gpu_bvh8_encode.d.ts +19 -0
  272. package/src/shade/renderer/gpu_primitive/bvh8/verify_gpu_bvh8_encode.d.ts.map +1 -0
  273. package/src/shade/renderer/gpu_primitive/bvh8/verify_gpu_bvh8_encode.js +361 -0
  274. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.d.ts.map +1 -1
  275. package/src/shade/renderer/grass/surface/shader_grass_surface_bake.js +5 -6
  276. package/src/shade/renderer/lightmap/GPULightMap.d.ts.map +1 -1
  277. package/src/shade/renderer/lightmap/GPULightMap.js +1 -0
  278. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  279. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +1 -0
  280. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
  281. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +1 -0
  282. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts +8 -4
  283. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
  284. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +60 -33
  285. package/src/shade/renderer/scene/verify_gpu_tlas_build.d.ts +6 -6
  286. package/src/shade/renderer/scene/verify_gpu_tlas_build.d.ts.map +1 -1
  287. package/src/shade/renderer/scene/verify_gpu_tlas_build.js +23 -53
  288. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/BVH8_INODE_STRUCT.d.ts +3 -1
  289. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/BVH8_INODE_STRUCT.d.ts.map +1 -1
  290. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/BVH8_INODE_STRUCT.js +5 -3
  291. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/BVH8_MAX_DEPTH.d.ts +28 -0
  292. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/BVH8_MAX_DEPTH.d.ts.map +1 -0
  293. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/BVH8_MAX_DEPTH.js +29 -0
  294. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_grid.d.ts +25 -0
  295. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_grid.d.ts.map +1 -0
  296. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_grid.js +97 -0
  297. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_intersect.d.ts +8 -0
  298. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_intersect.d.ts.map +1 -1
  299. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_intersect.js +23 -14
  300. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_slot_distances.d.ts +36 -0
  301. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_slot_distances.d.ts.map +1 -0
  302. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_node_slot_distances.js +222 -0
  303. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_ray_direction_rcp.d.ts +20 -0
  304. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_ray_direction_rcp.d.ts.map +1 -0
  305. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_ray_direction_rcp.js +30 -0
  306. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_tree_read.d.ts +30 -0
  307. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_tree_read.d.ts.map +1 -0
  308. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_tree_read.js +91 -0
  309. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_waiting_slots.d.ts +23 -0
  310. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_waiting_slots.d.ts.map +1 -0
  311. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_waiting_slots.js +40 -0
  312. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_blas_nearest.d.ts +22 -0
  313. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_blas_nearest.d.ts.map +1 -1
  314. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_blas_nearest.js +121 -70
  315. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_blas_occluded.d.ts +13 -1
  316. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_blas_occluded.d.ts.map +1 -1
  317. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_blas_occluded.js +117 -61
  318. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest.d.ts +21 -0
  319. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest.d.ts.map +1 -1
  320. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest.js +111 -72
  321. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts +1 -2
  322. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.d.ts.map +1 -1
  323. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_nearest_among.js +83 -54
  324. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_occluded.d.ts +14 -0
  325. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_occluded.d.ts.map +1 -1
  326. package/src/shade/renderer/shader/chunk/geometry/ray/chunk_ray_query_occluded.js +92 -60
  327. package/src/shade/renderer/shader/chunk/sdf/chunk_point_query_blas_nearest.d.ts +22 -0
  328. package/src/shade/renderer/shader/chunk/sdf/chunk_point_query_blas_nearest.d.ts.map +1 -1
  329. package/src/shade/renderer/shader/chunk/sdf/chunk_point_query_blas_nearest.js +153 -106
  330. package/src/shade/renderer/shader/chunk/sdf/chunk_scene_point_query_nearest.d.ts +21 -0
  331. package/src/shade/renderer/shader/chunk/sdf/chunk_scene_point_query_nearest.d.ts.map +1 -1
  332. package/src/shade/renderer/shader/chunk/sdf/chunk_scene_point_query_nearest.js +115 -74
  333. package/src/shade/renderer/shader/chunk/sdf/chunk_scene_sdf_raymarch.d.ts +5 -2
  334. package/src/shade/renderer/shader/chunk/sdf/chunk_scene_sdf_raymarch.d.ts.map +1 -1
  335. package/src/shade/renderer/shader/chunk/sdf/chunk_scene_sdf_raymarch.js +89 -63
  336. package/src/shade/renderer/shader/chunk/sdf/chunk_sdf_query_distance.d.ts +15 -0
  337. package/src/shade/renderer/shader/chunk/sdf/chunk_sdf_query_distance.d.ts.map +1 -1
  338. package/src/shade/renderer/shader/chunk/sdf/chunk_sdf_query_distance.js +108 -63
  339. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_raycast.d.ts +14 -0
  340. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_raycast.d.ts.map +1 -1
  341. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_raycast.js +94 -60
  342. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.d.ts.map +1 -1
  343. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.js +4 -4
  344. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +2 -2
  345. package/src/shade/renderer/water/query/shader_water_surface_query.d.ts.map +1 -1
  346. package/src/shade/renderer/water/query/shader_water_surface_query.js +227 -227
  347. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.d.ts.map +1 -1
  348. package/src/shade/renderer/water/surface/particles/chunk_water_particle_step.js +7 -4
  349. package/src/shade/renderer/water/surface/particles/shader_water_particles.js +2 -2
  350. package/src/shade/renderer/wind/GPUSceneWind.d.ts +36 -4
  351. package/src/shade/renderer/wind/GPUSceneWind.d.ts.map +1 -1
  352. package/src/shade/renderer/wind/GPUSceneWind.js +150 -4
  353. package/src/shade/renderer/wind/WIND_FIELD_STRUCT.d.ts +9 -3
  354. package/src/shade/renderer/wind/WIND_FIELD_STRUCT.d.ts.map +1 -1
  355. package/src/shade/renderer/wind/WIND_FIELD_STRUCT.js +13 -3
  356. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.d.ts +6 -6
  357. package/src/shade/renderer/wind/WIND_FIELD_UNIFORM.js +6 -6
  358. package/src/shade/renderer/wind/chunk_fluid_turbulence.d.ts +39 -0
  359. package/src/shade/renderer/wind/chunk_fluid_turbulence.d.ts.map +1 -0
  360. package/src/shade/renderer/wind/chunk_fluid_turbulence.js +63 -0
  361. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts +10 -7
  362. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.d.ts.map +1 -1
  363. package/src/shade/renderer/wind/chunk_wind_field_sample_cascades.js +34 -11
  364. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.d.ts +2 -2
  365. package/src/shade/renderer/wind/chunk_wind_field_sample_uniform.js +2 -2
  366. package/src/shade/renderer/wind/wind_field_add_resources.d.ts +6 -4
  367. package/src/shade/renderer/wind/wind_field_add_resources.d.ts.map +1 -1
  368. package/src/shade/renderer/wind/wind_field_add_resources.js +9 -6
  369. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_TARGET_STRUCT.d.ts +0 -18
  370. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_TARGET_STRUCT.d.ts.map +0 -1
  371. package/src/shade/renderer/geometry/bvh/BLAS_REFIT_TARGET_STRUCT.js +0 -50
  372. package/src/shade/renderer/geometry/bvh/bvh2_derive_topology.d.ts +0 -23
  373. package/src/shade/renderer/geometry/bvh/bvh2_derive_topology.d.ts.map +0 -1
  374. package/src/shade/renderer/geometry/bvh/bvh2_derive_topology.js +0 -87
  375. package/src/shade/renderer/geometry/bvh/shader_blas_refit_batch.d.ts +0 -42
  376. package/src/shade/renderer/geometry/bvh/shader_blas_refit_batch.d.ts.map +0 -1
  377. package/src/shade/renderer/geometry/bvh/shader_blas_refit_batch.js +0 -141
  378. package/src/shade/renderer/geometry/bvh/shader_blas_refit_batch_seed.d.ts +0 -21
  379. package/src/shade/renderer/geometry/bvh/shader_blas_refit_batch_seed.d.ts.map +0 -1
  380. package/src/shade/renderer/geometry/bvh/shader_blas_refit_batch_seed.js +0 -75
  381. package/src/shade/renderer/geometry/meshlet_batch_to_bvh.d.ts +0 -17
  382. package/src/shade/renderer/geometry/meshlet_batch_to_bvh.d.ts.map +0 -1
  383. package/src/shade/renderer/geometry/meshlet_batch_to_bvh.js +0 -247
  384. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_ray_query_blas_occlusion.d.ts +0 -7
  385. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_ray_query_blas_occlusion.d.ts.map +0 -1
  386. package/src/shade/renderer/shader/chunk/bvh/bvh8/webgpu/chunk_bvh8_ray_query_blas_occlusion.js +0 -226
  387. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mask_visibility_via_rays.d.ts +0 -8
  388. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mask_visibility_via_rays.d.ts.map +0 -1
  389. package/src/shade/renderer/shader/chunk/light/lpv/chunk_lpv_mask_visibility_via_rays.js +0 -45
  390. package/src/shade/renderer/shader/chunk/light/sample/chunk_ray_sample_light.d.ts +0 -3
  391. package/src/shade/renderer/shader/chunk/light/sample/chunk_ray_sample_light.d.ts.map +0 -1
  392. package/src/shade/renderer/shader/chunk/light/sample/chunk_ray_sample_light.js +0 -105
  393. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_distance.d.ts +0 -6
  394. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_distance.d.ts.map +0 -1
  395. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_distance.js +0 -86
  396. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_raycast_traversal_count.d.ts +0 -2
  397. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_raycast_traversal_count.d.ts.map +0 -1
  398. package/src/shade/renderer/shadow/sdf/chunk_ray_query_sdf_raycast_traversal_count.js +0 -136
@@ -0,0 +1,348 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { bitCount } from "../../../../core/binary/operations/bitCount.js";
3
+ import { BVH8_SLOT_UNASSIGNED, bvh8_assign_slots_greedy } from "../../../../core/bvh8/build/bvh8_assign_slots_greedy.js";
4
+ import { bvh8_node_fit_slots } from "../../../../core/bvh8/build/bvh8_node_fit_slots.js";
5
+ import { BVH8 } from "../../../../core/bvh8/BVH8.js";
6
+ import {
7
+ BVH_HEADER_WORDS,
8
+ BVH_NODE_BOUNDS_WORD,
9
+ BVH_NODE_CHILD_1_WORD,
10
+ BVH_NODE_CHILD_2_WORD,
11
+ BVH_NODE_STRIDE_WORDS
12
+ } from "../bvh/BVH_BUFFER_LAYOUT.js";
13
+ import {
14
+ BVH8_TREE_BOUNDS_HIGH_WORD,
15
+ BVH8_TREE_BOUNDS_LOW_WORD,
16
+ BVH8_TREE_HEADER_WORDS,
17
+ BVH8_TREE_LEAF_COUNT_WORD,
18
+ BVH8_TREE_NODE_STRIDE_WORDS,
19
+ BVH8_TREE_ORDER_OFFSET_WORD,
20
+ bvh8_tree_buffer_size
21
+ } from "./BVH8_TREE_BUFFER_LAYOUT.js";
22
+
23
+ const BVH_NULL_NODE = 0xFFFFFFFF;
24
+
25
+ const fround = Math.fround;
26
+
27
+ /**
28
+ * Binary levels a node of the wide tree stands for.
29
+ * @type {number}
30
+ */
31
+ export const BVH8_ENCODE_LEVELS = 3;
32
+
33
+ /**
34
+ * Encode a binary BVH as a BVH8, three binary levels to a node. The reference for the passes that do
35
+ * it on the GPU: they are held to this, word for word.
36
+ *
37
+ * **Which nodes.** An internal node of the binary tree whose depth is a multiple of three becomes a
38
+ * node of the wide tree; the root is at depth 0. Its children are what lies three levels below it,
39
+ * and the leaves met sooner: at most eight, taken child 1 before child 2 all the way down. One of
40
+ * them that is internal is itself at a depth that is a multiple of three, so it is a node too; one
41
+ * that is a leaf fills a leaf slot. A binary tree that is a single leaf is one node with that leaf
42
+ * in a slot.
43
+ *
44
+ * **Which slots.** A node's children are given its slots by {@link bvh8_assign_slots_greedy}: a
45
+ * child costs a slot the distance its box's centre lies from the node's centre, taken with the
46
+ * slot's signs — the bit of an axis set where the child should lie high on it. The cost is worked
47
+ * in single precision as `(±x + ±y) + ±z`, where an axis's term is the child's `low + high` less the
48
+ * node's. A child whose box is not a number costs nothing a comparison can rank: it takes the lowest
49
+ * slot left once the others have theirs, so that a tree's shape never depends on what its boxes hold.
50
+ *
51
+ * **Where.** Counting a node's child nodes and its leaves, in the order of the binary tree's own
52
+ * node indices: a node's child nodes are the block that starts at one more than the child nodes of
53
+ * every node before it, in slot order; its leaves' payloads are the run that starts at the leaves
54
+ * of every node before it, in slot order. The root is node 0. So a node finds its own place from
55
+ * its parent's block and the slot it has there, with no word from the parent.
56
+ *
57
+ * **Boxes.** A node's box is the binary node's, and its slots are fitted to it by
58
+ * {@link bvh8_node_fit_slots}.
59
+ *
60
+ * @param {Uint32Array} out the BVH8, in the layout of {@link BVH8_TREE_BUFFER_LAYOUT}; at least
61
+ * `bvh8_tree_buffer_size(leaf_count) / 4` words. Words past the tree written are left as they are.
62
+ * @param {Uint32Array} tree the binary tree as u32 words, `[root][node 0][node 1] ...`, in the layout
63
+ * of {@link BVH_BUFFER_LAYOUT}: `2 * leaf_count - 1` nodes, fitted
64
+ * @param {number} leaf_count
65
+ * @returns {number} nodes in the BVH8
66
+ */
67
+ export function bvh8_encode_from_bvh2(out, tree, leaf_count) {
68
+ assert.isNonNegativeInteger(leaf_count, 'leaf_count');
69
+ assert.greaterThanOrEqual(out.length * Uint32Array.BYTES_PER_ELEMENT, bvh8_tree_buffer_size(leaf_count), 'out is too small for a tree of this many leaves');
70
+
71
+ const out_floats = new Float32Array(out.buffer, out.byteOffset, out.length);
72
+
73
+ if (leaf_count === 0) {
74
+ out.fill(0, 0, BVH8_TREE_HEADER_WORDS + BVH8_TREE_NODE_STRIDE_WORDS);
75
+
76
+ out[BVH8_TREE_ORDER_OFFSET_WORD] = BVH8_TREE_HEADER_WORDS + BVH8_TREE_NODE_STRIDE_WORDS;
77
+
78
+ return 1;
79
+ }
80
+
81
+ const binary_count = 2 * leaf_count - 1;
82
+
83
+ assert.greaterThanOrEqual(tree.length, BVH_HEADER_WORDS + binary_count * BVH_NODE_STRIDE_WORDS, 'tree is too short for this many leaves');
84
+
85
+ const tree_floats = new Float32Array(tree.buffer, tree.byteOffset, tree.length);
86
+
87
+ const root = tree[0];
88
+
89
+ const child_1 = node => tree[BVH_HEADER_WORDS + node * BVH_NODE_STRIDE_WORDS + BVH_NODE_CHILD_1_WORD];
90
+ const child_2 = node => tree[BVH_HEADER_WORDS + node * BVH_NODE_STRIDE_WORDS + BVH_NODE_CHILD_2_WORD];
91
+ const is_leaf = node => child_1(node) === BVH_NULL_NODE;
92
+ const bounds_word = node => BVH_HEADER_WORDS + node * BVH_NODE_STRIDE_WORDS + BVH_NODE_BOUNDS_WORD;
93
+
94
+ // ===========
95
+ // Parents, and from them each node's depth
96
+ // ===========
97
+ const parents = new Uint32Array(binary_count).fill(BVH_NULL_NODE);
98
+
99
+ for (let node = 0; node < binary_count; node++) {
100
+ if (!is_leaf(node)) {
101
+ parents[child_1(node)] = node;
102
+ parents[child_2(node)] = node;
103
+ }
104
+ }
105
+
106
+ const depth_of = node => {
107
+ let depth = 0;
108
+
109
+ for (let at = parents[node]; at !== BVH_NULL_NODE; at = parents[at]) {
110
+ depth++;
111
+ }
112
+
113
+ return depth;
114
+ };
115
+
116
+ // an internal node at a depth that is a multiple of three, or a root that is the tree's only leaf
117
+ const is_wide = node => node === root
118
+ || (!is_leaf(node) && depth_of(node) % BVH8_ENCODE_LEVELS === 0);
119
+
120
+ /**
121
+ * The children of a wide node, child 1 before child 2 all the way down.
122
+ *
123
+ * @param {number} node
124
+ * @param {number[]} children output
125
+ * @returns {number} how many
126
+ */
127
+ function gather(node, children) {
128
+ let count = 0;
129
+
130
+ if (is_leaf(node)) {
131
+ // the tree is this one leaf
132
+ children[count++] = node;
133
+
134
+ return count;
135
+ }
136
+
137
+ const visit = (at, level) => {
138
+ if (is_leaf(at) || level === BVH8_ENCODE_LEVELS) {
139
+ children[count++] = at;
140
+
141
+ return;
142
+ }
143
+
144
+ visit(child_1(at), level + 1);
145
+ visit(child_2(at), level + 1);
146
+ };
147
+
148
+ visit(child_1(node), 1);
149
+ visit(child_2(node), 1);
150
+
151
+ return count;
152
+ }
153
+
154
+ const cost = new Float32Array(64);
155
+
156
+ /**
157
+ * The slot each child of a wide node is given.
158
+ *
159
+ * @param {number} node
160
+ * @param {number[]} children
161
+ * @param {number} child_count
162
+ * @param {Uint32Array} slots output, a slot a child
163
+ */
164
+ function assign(node, children, child_count, slots) {
165
+ const n = bounds_word(node);
166
+
167
+ for (let c = 0; c < child_count; c++) {
168
+ const b = bounds_word(children[c]);
169
+
170
+ const x = fround(fround(tree_floats[b] + tree_floats[b + 3]) - fround(tree_floats[n] + tree_floats[n + 3]));
171
+ const y = fround(fround(tree_floats[b + 1] + tree_floats[b + 4]) - fround(tree_floats[n + 1] + tree_floats[n + 4]));
172
+ const z = fround(fround(tree_floats[b + 2] + tree_floats[b + 5]) - fround(tree_floats[n + 2] + tree_floats[n + 5]));
173
+
174
+ for (let s = 0; s < 8; s++) {
175
+ cost[c * 8 + s] = fround(fround(((s & 4) !== 0 ? -x : x) + ((s & 2) !== 0 ? -y : y)) + ((s & 1) !== 0 ? -z : z));
176
+ }
177
+ }
178
+
179
+ let filled = bvh8_assign_slots_greedy(slots, cost, child_count);
180
+
181
+ // whoever no cost could rank, in order, into the lowest slots left
182
+ for (let c = 0; c < child_count; c++) {
183
+ if (slots[c] !== BVH8_SLOT_UNASSIGNED) {
184
+ continue;
185
+ }
186
+
187
+ let slot = 0;
188
+
189
+ while ((filled & (1 << slot)) !== 0) {
190
+ slot++;
191
+ }
192
+
193
+ slots[c] = slot;
194
+ filled |= 1 << slot;
195
+ }
196
+ }
197
+
198
+ // ===========
199
+ // Count each wide node's child nodes and leaves, and lay the blocks and runs end to end in the
200
+ // order of the binary tree's node indices
201
+ // ===========
202
+ const child_base = new Uint32Array(binary_count);
203
+ const leaf_base = new Uint32Array(binary_count);
204
+
205
+ const children = new Array(8);
206
+ const slots = new Uint32Array(8);
207
+
208
+ let node_count = 1;
209
+ let leaves_placed = 0;
210
+
211
+ for (let node = 0; node < binary_count; node++) {
212
+ child_base[node] = node_count;
213
+ leaf_base[node] = leaves_placed;
214
+
215
+ if (!is_wide(node)) {
216
+ continue;
217
+ }
218
+
219
+ const child_count = gather(node, children);
220
+
221
+ for (let c = 0; c < child_count; c++) {
222
+ if (is_leaf(children[c])) {
223
+ leaves_placed++;
224
+ } else {
225
+ node_count++;
226
+ }
227
+ }
228
+ }
229
+
230
+ assert.equal(leaves_placed, leaf_count, 'every leaf is the child of one wide node');
231
+
232
+ const order_offset = BVH8_TREE_HEADER_WORDS + node_count * BVH8_TREE_NODE_STRIDE_WORDS;
233
+
234
+ // ===========
235
+ // Write each wide node at the place its parent's block gives it
236
+ // ===========
237
+ const bvh8 = new BVH8();
238
+
239
+ bvh8.allocate_nodes(node_count);
240
+
241
+ const parent_children = new Array(8);
242
+ const parent_slots = new Uint32Array(8);
243
+
244
+ const slot_boxes = new Float32Array(48);
245
+
246
+ for (let node = 0; node < binary_count; node++) {
247
+ if (!is_wide(node)) {
248
+ continue;
249
+ }
250
+
251
+ let index = 0;
252
+
253
+ if (node !== root) {
254
+ // three levels up is the wide node this one is a child of; its place there is the block's
255
+ // start and the child nodes in the slots below its own
256
+ const parent = parents[parents[parents[node]]];
257
+
258
+ const sibling_count = gather(parent, parent_children);
259
+
260
+ assign(parent, parent_children, sibling_count, parent_slots);
261
+
262
+ let slot = 0;
263
+ let internal = 0;
264
+
265
+ for (let c = 0; c < sibling_count; c++) {
266
+ if (parent_children[c] === node) {
267
+ slot = parent_slots[c];
268
+ }
269
+
270
+ if (!is_leaf(parent_children[c])) {
271
+ internal |= 1 << parent_slots[c];
272
+ }
273
+ }
274
+
275
+ index = child_base[parent] + bitCount(internal & ((1 << slot) - 1));
276
+ }
277
+
278
+ const child_count = gather(node, children);
279
+
280
+ assign(node, children, child_count, slots);
281
+
282
+ let occupied = 0;
283
+ let internal = 0;
284
+
285
+ // which child sits in a slot
286
+ const child_in_slot = new Array(8).fill(BVH_NULL_NODE);
287
+
288
+ for (let c = 0; c < child_count; c++) {
289
+ const slot = slots[c];
290
+
291
+ occupied |= 1 << slot;
292
+ child_in_slot[slot] = children[c];
293
+
294
+ if (!is_leaf(children[c])) {
295
+ internal |= 1 << slot;
296
+ }
297
+
298
+ const b = bounds_word(children[c]);
299
+
300
+ for (let i = 0; i < 6; i++) {
301
+ slot_boxes[slot * 6 + i] = tree_floats[b + i];
302
+ }
303
+ }
304
+
305
+ const n = bounds_word(node);
306
+
307
+ bvh8_node_fit_slots(bvh8, index, tree_floats.subarray(n, n + 6), slot_boxes, occupied);
308
+
309
+ bvh8.node_set_imask(index, internal);
310
+ bvh8.node_set_base_index_child(index, child_base[node]);
311
+ bvh8.node_set_base_index_triangle(index, leaf_base[node]);
312
+
313
+ let leaves_before = 0;
314
+
315
+ for (let slot = 0; slot < 8; slot++) {
316
+ if ((occupied & (1 << slot)) === 0) {
317
+ bvh8.node_set_meta(index, slot, 0);
318
+ } else if ((internal & (1 << slot)) !== 0) {
319
+ bvh8.node_set_meta(index, slot, 0b00100000 | (24 + slot));
320
+ } else {
321
+ // a run of one leaf
322
+ bvh8.node_set_meta(index, slot, 0b00100000 | leaves_before);
323
+
324
+ // a binary leaf's second child word is its payload
325
+ out[order_offset + leaf_base[node] + leaves_before] = child_2(child_in_slot[slot]);
326
+
327
+ leaves_before++;
328
+ }
329
+ }
330
+ }
331
+
332
+ // ===========
333
+ // The header, and the nodes behind it
334
+ // ===========
335
+ const r = bounds_word(root);
336
+
337
+ for (let axis = 0; axis < 3; axis++) {
338
+ out_floats[BVH8_TREE_BOUNDS_LOW_WORD + axis] = tree_floats[r + axis];
339
+ out_floats[BVH8_TREE_BOUNDS_HIGH_WORD + axis] = tree_floats[r + 3 + axis];
340
+ }
341
+
342
+ out[BVH8_TREE_LEAF_COUNT_WORD] = leaf_count;
343
+ out[BVH8_TREE_ORDER_OFFSET_WORD] = order_offset;
344
+
345
+ out.set(new Uint32Array(bvh8.data_buffer, 0, node_count * BVH8_TREE_NODE_STRIDE_WORDS), BVH8_TREE_HEADER_WORDS);
346
+
347
+ return node_count;
348
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Check a BVH8 in the layout of {@link BVH8_TREE_BUFFER_LAYOUT} for what every walk of it relies on,
3
+ * whoever built it:
4
+ *
5
+ * - the header counts the leaves and says where their payloads start, straight behind the nodes;
6
+ * - from node 0 every node is reached, and reached once;
7
+ * - a slot is empty, a child node or one leaf, and its meta and the node's child mask agree which;
8
+ * - a node's child nodes are the block its base names, in slot order;
9
+ * - every payload expected is a leaf's, once, and no leaf carries another;
10
+ * - a slot's box holds every leaf under it, to within the node's grid: no leaf sticks out, and the
11
+ * box is no looser than two steps of the grid a side;
12
+ * - the header's box is the box of every leaf.
13
+ *
14
+ * A tree of no leaves is a header that counts none and one node with every slot empty.
15
+ *
16
+ * @param {ArrayLike<number>} tree the buffer's words
17
+ * @param {number} leaf_count how many leaves the tree should hold
18
+ * @param {ArrayLike<number>} expected_payloads the payload of every leaf, in any order
19
+ * @param {function(number): ArrayLike<number>} [box_of] the box of the leaf carrying a payload,
20
+ * `[min.xyz, max.xyz]` in float32 values; without it no box is checked
21
+ * @returns {{problems: string[], warnings: string[], node_count: number, max_depth: number, depth_histogram: number[], low: number[], high: number[]}}
22
+ * what is wrong, the first three of a kind; then the tree's nodes, the levels of nodes a walk goes
23
+ * down, its leaves by the level of the node that holds them, and the header's box
24
+ */
25
+ export function check_bvh8_tree(tree: ArrayLike<number>, leaf_count: number, expected_payloads: ArrayLike<number>, box_of?: (arg0: number) => ArrayLike<number>): {
26
+ problems: string[];
27
+ warnings: string[];
28
+ node_count: number;
29
+ max_depth: number;
30
+ depth_histogram: number[];
31
+ low: number[];
32
+ high: number[];
33
+ };
34
+ //# sourceMappingURL=check_bvh8_tree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check_bvh8_tree.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/gpu_primitive/bvh8/check_bvh8_tree.js"],"names":[],"mappings":"AAWA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,sCATW,UAAU,MAAM,CAAC,cACjB,MAAM,qBACN,UAAU,MAAM,CAAC,kBACR,MAAM,KAAG,UAAU,MAAM,CAAC,GAEjC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAC,CAmQrJ"}
@@ -0,0 +1,291 @@
1
+ import { bitCount } from "../../../../core/binary/operations/bitCount.js";
2
+ import { reinterpret_uint32_as_float32 } from "../../../../core/binary/reinterpret_uint32_as_float32.js";
3
+ import {
4
+ BVH8_TREE_BOUNDS_HIGH_WORD,
5
+ BVH8_TREE_BOUNDS_LOW_WORD,
6
+ BVH8_TREE_HEADER_WORDS,
7
+ BVH8_TREE_LEAF_COUNT_WORD,
8
+ BVH8_TREE_NODE_STRIDE_WORDS,
9
+ BVH8_TREE_ORDER_OFFSET_WORD
10
+ } from "./BVH8_TREE_BUFFER_LAYOUT.js";
11
+
12
+ /**
13
+ * Check a BVH8 in the layout of {@link BVH8_TREE_BUFFER_LAYOUT} for what every walk of it relies on,
14
+ * whoever built it:
15
+ *
16
+ * - the header counts the leaves and says where their payloads start, straight behind the nodes;
17
+ * - from node 0 every node is reached, and reached once;
18
+ * - a slot is empty, a child node or one leaf, and its meta and the node's child mask agree which;
19
+ * - a node's child nodes are the block its base names, in slot order;
20
+ * - every payload expected is a leaf's, once, and no leaf carries another;
21
+ * - a slot's box holds every leaf under it, to within the node's grid: no leaf sticks out, and the
22
+ * box is no looser than two steps of the grid a side;
23
+ * - the header's box is the box of every leaf.
24
+ *
25
+ * A tree of no leaves is a header that counts none and one node with every slot empty.
26
+ *
27
+ * @param {ArrayLike<number>} tree the buffer's words
28
+ * @param {number} leaf_count how many leaves the tree should hold
29
+ * @param {ArrayLike<number>} expected_payloads the payload of every leaf, in any order
30
+ * @param {function(number): ArrayLike<number>} [box_of] the box of the leaf carrying a payload,
31
+ * `[min.xyz, max.xyz]` in float32 values; without it no box is checked
32
+ * @returns {{problems: string[], warnings: string[], node_count: number, max_depth: number, depth_histogram: number[], low: number[], high: number[]}}
33
+ * what is wrong, the first three of a kind; then the tree's nodes, the levels of nodes a walk goes
34
+ * down, its leaves by the level of the node that holds them, and the header's box
35
+ */
36
+ export function check_bvh8_tree(tree, leaf_count, expected_payloads, box_of) {
37
+ const words = tree instanceof Uint32Array ? tree : Uint32Array.from(tree);
38
+ const floats = new Float32Array(words.buffer, words.byteOffset, words.length);
39
+
40
+ /** @type {string[]} */
41
+ const problems = [];
42
+
43
+ const result = {
44
+ problems,
45
+ warnings: [],
46
+ node_count: 0,
47
+ max_depth: 0,
48
+ depth_histogram: [],
49
+ low: Array.from(floats.subarray(BVH8_TREE_BOUNDS_LOW_WORD, BVH8_TREE_BOUNDS_LOW_WORD + 3)),
50
+ high: Array.from(floats.subarray(BVH8_TREE_BOUNDS_HIGH_WORD, BVH8_TREE_BOUNDS_HIGH_WORD + 3)),
51
+ };
52
+
53
+ // how many problems of each kind have been met
54
+ const met = new Map();
55
+
56
+ // of a kind of problem, the first few say it; a tree wrong at the root is wrong everywhere below
57
+ function problem(kind, text) {
58
+ const count = (met.get(kind) ?? 0) + 1;
59
+
60
+ met.set(kind, count);
61
+
62
+ if (count <= 3) {
63
+ problems.push(text);
64
+ }
65
+ }
66
+
67
+ if (words[BVH8_TREE_LEAF_COUNT_WORD] !== leaf_count) {
68
+ problem('header', `the header counts ${words[BVH8_TREE_LEAF_COUNT_WORD]} leaves, expected ${leaf_count}`);
69
+ }
70
+
71
+ const order_offset = words[BVH8_TREE_ORDER_OFFSET_WORD];
72
+
73
+ const node_words = order_offset - BVH8_TREE_HEADER_WORDS;
74
+
75
+ if (order_offset < BVH8_TREE_HEADER_WORDS + BVH8_TREE_NODE_STRIDE_WORDS || node_words % BVH8_TREE_NODE_STRIDE_WORDS !== 0) {
76
+ problem('header', `the payloads start at word ${order_offset}, which is not behind a whole number of nodes`);
77
+
78
+ return result;
79
+ }
80
+
81
+ if (order_offset + leaf_count > words.length) {
82
+ problem('header', `the payloads start at word ${order_offset} and ${leaf_count} of them run past the buffer's ${words.length} words`);
83
+
84
+ return result;
85
+ }
86
+
87
+ const node_count = node_words / BVH8_TREE_NODE_STRIDE_WORDS;
88
+
89
+ result.node_count = node_count;
90
+
91
+ if (leaf_count === 0) {
92
+ if (node_count !== 1) {
93
+ problem('empty', `a tree of no leaves has ${node_count} nodes, expected the one empty node`);
94
+ }
95
+
96
+ for (let word = 0; word < BVH8_TREE_NODE_STRIDE_WORDS; word++) {
97
+ if (words[BVH8_TREE_HEADER_WORDS + word] !== 0) {
98
+ problem('empty', `the empty node's word ${word} is 0x${words[BVH8_TREE_HEADER_WORDS + word].toString(16)}, expected zero`);
99
+ }
100
+ }
101
+
102
+ return result;
103
+ }
104
+
105
+ const visited = new Uint8Array(node_count);
106
+
107
+ // how many leaves carry each payload
108
+ const carried = new Map();
109
+
110
+ /**
111
+ * @param {number} node
112
+ * @param {number} depth levels of nodes down to and including this one
113
+ * @returns {number[]|null} the box of every leaf under the node, if boxes are known
114
+ */
115
+ function walk(node, depth) {
116
+ if (visited[node] === 1) {
117
+ problem('reached twice', `node ${node} is reached twice`);
118
+
119
+ return null;
120
+ }
121
+
122
+ visited[node] = 1;
123
+
124
+ result.max_depth = Math.max(result.max_depth, depth);
125
+
126
+ const at = BVH8_TREE_HEADER_WORDS + node * BVH8_TREE_NODE_STRIDE_WORDS;
127
+
128
+ const origin = [floats[at], floats[at + 1], floats[at + 2]];
129
+
130
+ const packed = words[at + 3];
131
+
132
+ // the step of the node's grid an axis; an exponent of zero is a step of nothing
133
+ const steps = [packed & 0xFF, (packed >>> 8) & 0xFF, (packed >>> 16) & 0xFF]
134
+ .map(exponent => exponent === 0 ? 0 : reinterpret_uint32_as_float32(exponent << 23));
135
+
136
+ const child_mask = packed >>> 24;
137
+
138
+ const child_base = words[at + 4];
139
+ const leaf_base = words[at + 5];
140
+
141
+ /** @type {number[]|null} */
142
+ let under_node = null;
143
+
144
+ let child_rank = 0;
145
+ let held = 0;
146
+
147
+ for (let slot = 0; slot < 8; slot++) {
148
+ const slot_meta = (words[at + 6 + (slot >> 2)] >>> ((slot & 3) * 8)) & 0xFF;
149
+
150
+ const is_child = (child_mask & (1 << slot)) !== 0;
151
+
152
+ if (slot_meta === 0) {
153
+ if (is_child) {
154
+ problem('meta', `node ${node} slot ${slot} is empty and in the child mask`);
155
+ }
156
+
157
+ continue;
158
+ }
159
+
160
+ held++;
161
+
162
+ /** @type {number[]|null} */
163
+ let under = null;
164
+
165
+ if (is_child) {
166
+ if (slot_meta !== (0b00100000 | (24 + slot))) {
167
+ problem('meta', `node ${node} slot ${slot} is in the child mask with meta 0x${slot_meta.toString(16)}`);
168
+ }
169
+
170
+ const child = child_base + child_rank;
171
+
172
+ child_rank++;
173
+
174
+ if (child >= node_count) {
175
+ problem('child', `node ${node} slot ${slot} names child node ${child} of ${node_count}`);
176
+
177
+ continue;
178
+ }
179
+
180
+ under = walk(child, depth + 1);
181
+ } else {
182
+ if (bitCount(slot_meta >>> 5) !== 1) {
183
+ problem('meta', `node ${node} slot ${slot} has meta 0x${slot_meta.toString(16)}, which is not one leaf`);
184
+
185
+ continue;
186
+ }
187
+
188
+ const leaf = leaf_base + (slot_meta & 31);
189
+
190
+ if (leaf >= leaf_count) {
191
+ problem('leaf', `node ${node} slot ${slot} names leaf ${leaf} of ${leaf_count}`);
192
+
193
+ continue;
194
+ }
195
+
196
+ const payload = words[order_offset + leaf];
197
+
198
+ carried.set(payload, (carried.get(payload) ?? 0) + 1);
199
+
200
+ result.depth_histogram[depth] = (result.depth_histogram[depth] ?? 0) + 1;
201
+
202
+ if (box_of !== undefined) {
203
+ under = Array.from(box_of(payload));
204
+ }
205
+ }
206
+
207
+ if (under === null) {
208
+ continue;
209
+ }
210
+
211
+ for (let axis = 0; axis < 3; axis++) {
212
+ const byte_shift = (slot & 3) * 8;
213
+
214
+ const q_low = (words[at + 8 + axis * 4 + (slot >> 2)] >>> byte_shift) & 0xFF;
215
+ const q_high = (words[at + 10 + axis * 4 + (slot >> 2)] >>> byte_shift) & 0xFF;
216
+
217
+ const low = origin[axis] + q_low * steps[axis];
218
+ const high = origin[axis] + q_high * steps[axis];
219
+
220
+ if (!(low <= under[axis] && high >= under[axis + 3])) {
221
+ problem('holds', `node ${node} slot ${slot} axis ${axis}: the box [${low}, ${high}] does not hold what is under it, [${under[axis]}, ${under[axis + 3]}]`);
222
+ } else if (under[axis] - low > 2 * steps[axis] || high - under[axis + 3] > 2 * steps[axis]) {
223
+ problem('loose', `node ${node} slot ${slot} axis ${axis}: the box [${low}, ${high}] is more than two steps of ${steps[axis]} looser than what is under it, [${under[axis]}, ${under[axis + 3]}]`);
224
+ }
225
+ }
226
+
227
+ if (under_node === null) {
228
+ under_node = under.slice();
229
+ } else {
230
+ for (let axis = 0; axis < 3; axis++) {
231
+ under_node[axis] = Math.min(under_node[axis], under[axis]);
232
+ under_node[axis + 3] = Math.max(under_node[axis + 3], under[axis + 3]);
233
+ }
234
+ }
235
+ }
236
+
237
+ if (held === 0) {
238
+ problem('nothing', `node ${node} holds nothing`);
239
+ }
240
+
241
+ return under_node;
242
+ }
243
+
244
+ const under_root = walk(0, 1);
245
+
246
+ let unreached = 0;
247
+
248
+ for (let node = 0; node < node_count; node++) {
249
+ if (visited[node] === 0) {
250
+ unreached++;
251
+ }
252
+ }
253
+
254
+ if (unreached > 0) {
255
+ problem('unreached', `${unreached} of ${node_count} nodes are never reached from the root`);
256
+ }
257
+
258
+ let leaves_expected = 0;
259
+
260
+ for (let i = 0; i < expected_payloads.length; i++) {
261
+ const payload = expected_payloads[i];
262
+
263
+ leaves_expected++;
264
+
265
+ const times = carried.get(payload) ?? 0;
266
+
267
+ if (times !== 1) {
268
+ problem('payload', `payload ${payload} is carried by ${times} leaves, expected one`);
269
+ }
270
+
271
+ carried.delete(payload);
272
+ }
273
+
274
+ for (const [payload, times] of carried) {
275
+ problem('stray payload', `payload ${payload}, which no leaf should carry, is carried by ${times}`);
276
+ }
277
+
278
+ if (leaves_expected !== leaf_count) {
279
+ problem('header', `${leaves_expected} payloads were expected of a tree of ${leaf_count} leaves`);
280
+ }
281
+
282
+ if (under_root !== null) {
283
+ for (let axis = 0; axis < 3; axis++) {
284
+ if (result.low[axis] !== under_root[axis] || result.high[axis] !== under_root[axis + 3]) {
285
+ problem('bounds', `the header's box on axis ${axis} is [${result.low[axis]}, ${result.high[axis]}], and the leaves span [${under_root[axis]}, ${under_root[axis + 3]}]`);
286
+ }
287
+ }
288
+ }
289
+
290
+ return result;
291
+ }