@woosh/meep-engine 3.24.0 → 3.25.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 (322) hide show
  1. package/package.json +1 -1
  2. package/src/engine/graphics3/PickingSystem.d.ts +3 -3
  3. package/src/engine/graphics3/PickingSystem.d.ts.map +1 -1
  4. package/src/engine/graphics3/PickingSystem.js +52 -55
  5. package/src/engine/graphics3/shader_picking_gather.d.ts +40 -0
  6. package/src/engine/graphics3/shader_picking_gather.d.ts.map +1 -0
  7. package/src/engine/graphics3/shader_picking_gather.js +120 -0
  8. package/src/engine/physics/cloth/MEASUREMENTS.md +1050 -0
  9. package/src/engine/physics/cloth/PLAN.md +411 -49
  10. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts +52 -0
  11. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.d.ts.map +1 -0
  12. package/src/engine/physics/cloth/ecs/ClothColliderSerializationAdapter.js +70 -0
  13. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts +12 -3
  14. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts.map +1 -1
  15. package/src/engine/physics/cloth/ecs/ClothDynamics.js +340 -331
  16. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +36 -22
  17. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -1
  18. package/src/engine/physics/cloth/ecs/ClothInstance.js +38 -23
  19. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts +84 -0
  20. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.d.ts.map +1 -0
  21. package/src/engine/physics/cloth/ecs/ClothSerializationAdapter.js +141 -0
  22. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +51 -82
  23. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -1
  24. package/src/engine/physics/cloth/ecs/ClothSystem.js +81 -869
  25. package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts +48 -0
  26. package/src/engine/physics/cloth/ecs/ClothWorkerCore.d.ts.map +1 -0
  27. package/src/engine/physics/cloth/ecs/ClothWorkerCore.js +107 -0
  28. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts +286 -0
  29. package/src/engine/physics/cloth/ecs/ClothWorld.d.ts.map +1 -0
  30. package/src/engine/physics/cloth/ecs/ClothWorld.js +1104 -0
  31. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts +182 -0
  32. package/src/engine/physics/cloth/ecs/WorkerClothSystem.d.ts.map +1 -0
  33. package/src/engine/physics/cloth/ecs/WorkerClothSystem.js +562 -0
  34. package/src/engine/physics/cloth/ecs/cloth.worker.d.ts +2 -0
  35. package/src/engine/physics/cloth/ecs/cloth.worker.d.ts.map +1 -0
  36. package/src/engine/physics/cloth/ecs/cloth.worker.js +12 -0
  37. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -1
  38. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +2 -1
  39. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +58 -0
  40. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -1
  41. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +341 -271
  42. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +20 -2
  43. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -1
  44. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +39 -15
  45. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts +29 -0
  46. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.d.ts.map +1 -0
  47. package/src/engine/physics/cloth/ecs/cloth_worker_protocol.js +78 -0
  48. package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts +17 -0
  49. package/src/engine/physics/cloth/ecs/makeClothWorker.d.ts.map +1 -0
  50. package/src/engine/physics/cloth/ecs/makeClothWorker.js +18 -0
  51. package/src/engine/physics/cloth/playground/README.md +73 -2
  52. package/src/engine/physics/cloth/playground/wind.html +166 -0
  53. package/src/engine/physics/cloth/playground/wind_build.d.ts +81 -0
  54. package/src/engine/physics/cloth/playground/wind_build.d.ts.map +1 -0
  55. package/src/engine/physics/cloth/playground/wind_build.js +141 -0
  56. package/src/engine/physics/cloth/playground/wind_main.d.ts +2 -0
  57. package/src/engine/physics/cloth/playground/wind_main.d.ts.map +1 -0
  58. package/src/engine/physics/cloth/playground/wind_main.js +681 -0
  59. package/src/engine/physics/cloth/solver/ClothState.d.ts +134 -79
  60. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -1
  61. package/src/engine/physics/cloth/solver/ClothState.js +273 -12
  62. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +33 -0
  63. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -1
  64. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +34 -0
  65. package/src/engine/physics/cloth/solver/cloth_step.d.ts +21 -2
  66. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -1
  67. package/src/engine/physics/cloth/solver/cloth_step.js +173 -4
  68. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts +40 -0
  69. package/src/engine/physics/cloth/solver/cloth_step_instance.d.ts.map +1 -0
  70. package/src/engine/physics/cloth/solver/cloth_step_instance.js +284 -0
  71. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts +107 -0
  72. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.d.ts.map +1 -0
  73. package/src/engine/physics/cloth/solver/cloth_wind_accelerate.js +263 -0
  74. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts +88 -0
  75. package/src/engine/physics/cloth/wind/AbstractClothWind.d.ts.map +1 -0
  76. package/src/engine/physics/cloth/wind/AbstractClothWind.js +100 -0
  77. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts +85 -0
  78. package/src/engine/physics/cloth/wind/ClothAmbientWind.d.ts.map +1 -0
  79. package/src/engine/physics/cloth/wind/ClothAmbientWind.js +182 -0
  80. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts +79 -0
  81. package/src/engine/physics/cloth/wind/ClothFluidWind.d.ts.map +1 -0
  82. package/src/engine/physics/cloth/wind/ClothFluidWind.js +210 -0
  83. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts +101 -0
  84. package/src/engine/physics/cloth/wind/cloth_sample_air.d.ts.map +1 -0
  85. package/src/engine/physics/cloth/wind/cloth_sample_air.js +290 -0
  86. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  87. package/src/shade/device/mock/texture_copy.d.ts.map +1 -1
  88. package/src/shade/device/mock/texture_copy.js +28 -0
  89. package/src/shade/playground/vgeo_runtime/README.md +94 -0
  90. package/src/shade/playground/vgeo_runtime/index.html +62 -0
  91. package/src/shade/playground/vgeo_runtime/main.d.ts +2 -0
  92. package/src/shade/playground/vgeo_runtime/main.d.ts.map +1 -0
  93. package/src/shade/playground/vgeo_runtime/main.js +399 -0
  94. package/src/shade/playground/vgeo_viewer/sample_asset.js +1 -1
  95. package/src/shade/renderer/camera/Camera.d.ts +14 -0
  96. package/src/shade/renderer/camera/Camera.d.ts.map +1 -1
  97. package/src/shade/renderer/camera/Camera.js +7 -1
  98. package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.d.ts.map +1 -1
  99. package/src/shade/renderer/geometry/GEOMETRY_METADATA_STRUCT.js +16 -1
  100. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  101. package/src/shade/renderer/geometry/GPUGeometryManager.js +29 -9
  102. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts +10 -0
  103. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts.map +1 -1
  104. package/src/shade/renderer/geometry/GPUGeometryMetadata.js +13 -0
  105. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +46 -18
  106. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +70 -22
  107. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +97 -19
  108. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts +12 -2
  109. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.d.ts.map +1 -1
  110. package/src/shade/renderer/geometry/virtual/build/VGeoBuildOptions.js +14 -3
  111. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts +2 -2
  112. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.d.ts.map +1 -1
  113. package/src/shade/renderer/geometry/virtual/build/vgeo_assemble_pages.js +124 -6
  114. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts +22 -0
  115. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.d.ts.map +1 -0
  116. package/src/shade/renderer/geometry/virtual/build/vgeo_page_node_count.js +53 -0
  117. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts +8 -1
  118. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.d.ts.map +1 -1
  119. package/src/shade/renderer/geometry/virtual/format/VGeoWriteOptions.js +8 -1
  120. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts +16 -0
  121. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.d.ts.map +1 -0
  122. package/src/shade/renderer/geometry/virtual/format/header/VGEO_FEATURE_IGNORABLE_BOUNDED_PAGES.js +15 -0
  123. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts +54 -0
  124. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.d.ts.map +1 -0
  125. package/src/shade/renderer/geometry/virtual/format/page/VGEO_DEFAULT_PAGE_SIZE.js +53 -0
  126. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts +13 -2
  127. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.d.ts.map +1 -1
  128. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerHeader.js +14 -2
  129. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts +100 -0
  130. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.d.ts.map +1 -1
  131. package/src/shade/renderer/geometry/virtual/format/read/VGeoPage.js +113 -0
  132. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.d.ts.map +1 -1
  133. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_header.js +5 -0
  134. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.d.ts.map +1 -1
  135. package/src/shade/renderer/geometry/virtual/format/read/vgeo_read_page.js +108 -0
  136. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.d.ts.map +1 -1
  137. package/src/shade/renderer/geometry/virtual/format/vgeo_validate_container.js +23 -0
  138. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.d.ts.map +1 -1
  139. package/src/shade/renderer/geometry/virtual/format/vgeo_write_container.js +40 -0
  140. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts +228 -0
  141. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.d.ts.map +1 -0
  142. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryPool.js +932 -0
  143. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts +176 -0
  144. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.d.ts.map +1 -0
  145. package/src/shade/renderer/geometry/virtual/runtime/GPUVirtualGeometryRuntime.js +425 -0
  146. package/src/shade/renderer/geometry/virtual/runtime/REVIEW_LEDGER.md +241 -0
  147. package/src/shade/renderer/geometry/virtual/runtime/RUNTIME_PLAN.md +574 -0
  148. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts +120 -0
  149. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.d.ts.map +1 -0
  150. package/src/shade/renderer/geometry/virtual/runtime/VGeoFeedbackLoop.js +674 -0
  151. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts +287 -0
  152. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.d.ts.map +1 -0
  153. package/src/shade/renderer/geometry/virtual/runtime/VGeoRuntimeTables.js +976 -0
  154. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts +42 -0
  155. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.d.ts.map +1 -0
  156. package/src/shade/renderer/geometry/virtual/runtime/VirtualGeometry.js +96 -0
  157. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts +16 -0
  158. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.d.ts.map +1 -0
  159. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_FLAG_CULL.js +15 -0
  160. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts +19 -0
  161. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.d.ts.map +1 -0
  162. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_PARAMS_STRUCT.js +83 -0
  163. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts +9 -0
  164. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.d.ts.map +1 -0
  165. package/src/shade/renderer/geometry/virtual/runtime/cut/VGEO_CUT_WORKGROUP_SIZE.js +8 -0
  166. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts +19 -0
  167. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.d.ts.map +1 -0
  168. package/src/shade/renderer/geometry/virtual/runtime/cut/chunk_vgeo_cut_access.js +160 -0
  169. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts +3 -0
  170. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.d.ts.map +1 -0
  171. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_collect.js +255 -0
  172. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts +3 -0
  173. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.d.ts.map +1 -0
  174. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_queue_prepare.js +80 -0
  175. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts +3 -0
  176. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.d.ts.map +1 -0
  177. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_seed.js +144 -0
  178. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts +3 -0
  179. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.d.ts.map +1 -0
  180. package/src/shade/renderer/geometry/virtual/runtime/cut/shader_vgeo_cut_traverse.js +530 -0
  181. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts +39 -0
  182. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.d.ts.map +1 -0
  183. package/src/shade/renderer/geometry/virtual/runtime/cut/vgeo_cut_feedback_layout.js +54 -0
  184. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts +41 -0
  185. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.d.ts.map +1 -0
  186. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_if_enabled.js +56 -0
  187. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts +49 -0
  188. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.d.ts.map +1 -0
  189. package/src/shade/renderer/geometry/virtual/runtime/graph_vgeo_expand_instances_to_meshlets.js +328 -0
  190. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts +10 -0
  191. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.d.ts.map +1 -0
  192. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_OFFSET.js +51 -0
  193. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts +13 -0
  194. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.d.ts.map +1 -0
  195. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CLUSTER_ROW_WORDS.js +12 -0
  196. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts +17 -0
  197. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.d.ts.map +1 -0
  198. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT.js +99 -0
  199. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts +9 -0
  200. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.d.ts.map +1 -0
  201. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_CUT_STAT_COUNT.js +8 -0
  202. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts +7 -0
  203. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.d.ts.map +1 -0
  204. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_FEEDBACK_OFFSET.js +27 -0
  205. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts +14 -0
  206. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.d.ts.map +1 -0
  207. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_NONE.js +13 -0
  208. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts +7 -0
  209. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.d.ts.map +1 -0
  210. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_OFFSET.js +35 -0
  211. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts +9 -0
  212. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.d.ts.map +1 -0
  213. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GEOMETRY_ROW_WORDS.js +8 -0
  214. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts +15 -0
  215. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.d.ts.map +1 -0
  216. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_FLAG_SELECTABLE.js +14 -0
  217. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts +7 -0
  218. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.d.ts.map +1 -0
  219. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_OFFSET.js +36 -0
  220. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts +10 -0
  221. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.d.ts.map +1 -0
  222. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_GROUP_ROW_WORDS.js +9 -0
  223. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts +11 -0
  224. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.d.ts.map +1 -0
  225. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_OFFSET.js +54 -0
  226. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts +9 -0
  227. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.d.ts.map +1 -0
  228. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_INSTANCE_WORDS.js +8 -0
  229. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts +11 -0
  230. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.d.ts.map +1 -0
  231. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_LEAF_BIT.js +10 -0
  232. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts +7 -0
  233. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.d.ts.map +1 -0
  234. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_OFFSET.js +30 -0
  235. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts +9 -0
  236. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.d.ts.map +1 -0
  237. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NODE_ROW_WORDS.js +8 -0
  238. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts +13 -0
  239. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.d.ts.map +1 -0
  240. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_NO_CHILD_PAGE.js +12 -0
  241. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts +14 -0
  242. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.d.ts.map +1 -0
  243. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_NONE.js +13 -0
  244. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts +11 -0
  245. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.d.ts.map +1 -0
  246. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_OFFSET.js +61 -0
  247. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts +9 -0
  248. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.d.ts.map +1 -0
  249. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_PAGE_ROW_WORDS.js +8 -0
  250. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts +13 -0
  251. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.d.ts.map +1 -0
  252. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_ITEM_WORDS.js +12 -0
  253. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts +9 -0
  254. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.d.ts.map +1 -0
  255. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_QUEUE_TARGET_GROUP_BIT.js +8 -0
  256. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts +20 -0
  257. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.d.ts.map +1 -0
  258. package/src/shade/renderer/geometry/virtual/runtime/layout/VGEO_WANT_WORDS.js +19 -0
  259. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts +23 -0
  260. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.d.ts.map +1 -0
  261. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_meshlet_batch.js +70 -0
  262. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts +18 -0
  263. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.d.ts.map +1 -0
  264. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_block_words.js +25 -0
  265. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts +35 -0
  266. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.d.ts.map +1 -0
  267. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_page_slot_words.js +41 -0
  268. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts +32 -0
  269. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.d.ts.map +1 -0
  270. package/src/shade/renderer/geometry/virtual/runtime/vgeo_runtime_write_page_block.js +138 -0
  271. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  272. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  273. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.d.ts.map +1 -1
  274. package/src/shade/renderer/rasterize/bucket/prepare_meshlet_draw_commands_by_material.js +9 -0
  275. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  276. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +12 -1
  277. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  278. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +12 -1
  279. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  280. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +10 -0
  281. package/src/shade/renderer/scene/GPUSceneContext.d.ts +18 -0
  282. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  283. package/src/shade/renderer/scene/GPUSceneContext.js +647 -594
  284. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  285. package/src/shade/renderer/scene/rows/GPUSceneRows.js +22 -0
  286. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts +35 -0
  287. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.d.ts.map +1 -0
  288. package/src/shade/renderer/shader/chunk/geometry/mat3/chunk_mat3_singular_values.js +91 -0
  289. package/src/engine/graphics3/instantiate_scene_bundle.d.ts +0 -54
  290. package/src/engine/graphics3/instantiate_scene_bundle.d.ts.map +0 -1
  291. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts +0 -39
  292. package/src/engine/graphics3/shade_node_to_entity_composition.d.ts.map +0 -1
  293. package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts +0 -35
  294. package/src/shade/renderer/animation/compute_skin_world_bounds.d.ts.map +0 -1
  295. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts +0 -96
  296. package/src/shade/renderer/animation/compute_skinned_mesh_clip_bounding_sphere.d.ts.map +0 -1
  297. package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts +0 -20
  298. package/src/shade/renderer/animation/pose/pose_evaluate_world.d.ts.map +0 -1
  299. package/src/shade/renderer/animation/skin_bind_transform.d.ts +0 -25
  300. package/src/shade/renderer/animation/skin_bind_transform.d.ts.map +0 -1
  301. package/src/shade/renderer/animation/skin_frame_defect.d.ts +0 -34
  302. package/src/shade/renderer/animation/skin_frame_defect.d.ts.map +0 -1
  303. package/src/shade/renderer/animation/skin_joint_matrix.d.ts +0 -24
  304. package/src/shade/renderer/animation/skin_joint_matrix.d.ts.map +0 -1
  305. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts +0 -70
  306. package/src/shade/renderer/animation/skin_normalize_mesh_frame.d.ts.map +0 -1
  307. package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts +0 -96
  308. package/src/shade/renderer/buffer/table/GPUReadbackStagingRing.d.ts.map +0 -1
  309. package/src/shade/renderer/scene/InstanceBatch.d.ts +0 -92
  310. package/src/shade/renderer/scene/InstanceBatch.d.ts.map +0 -1
  311. package/src/shade/renderer/scene/Mesh.d.ts +0 -55
  312. package/src/shade/renderer/scene/Mesh.d.ts.map +0 -1
  313. package/src/shade/renderer/scene/Node3D.d.ts +0 -159
  314. package/src/shade/renderer/scene/Node3D.d.ts.map +0 -1
  315. package/src/shade/renderer/scene/Node3DDirtyList.d.ts +0 -59
  316. package/src/shade/renderer/scene/Node3DDirtyList.d.ts.map +0 -1
  317. package/src/shade/renderer/scene/SceneNodeBinding.d.ts +0 -52
  318. package/src/shade/renderer/scene/SceneNodeBinding.d.ts.map +0 -1
  319. package/src/shade/renderer/scene/SkinnedMesh.d.ts +0 -27
  320. package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +0 -1
  321. package/src/shade/renderer/scene/TransformAuthority.d.ts +0 -19
  322. package/src/shade/renderer/scene/TransformAuthority.d.ts.map +0 -1
@@ -1,594 +1,647 @@
1
- import { assert } from "../../../core/assert.js";
2
- import { ShadeGPUCommandContext } from "../../device/ShadeGPUCommandContext.js";
3
- import { GPUAnimationManager } from "../animation/GPUAnimationManager.js";
4
- import { GPUDatabase } from "../buffer/table/GPUDatabase.js";
5
- import { Brick4LightMap } from "../global_illumination/brick4/Brick4LightMap.js";
6
- import { GPULightProbeVolume } from "../global_illumination/lpv/GPULightProbeVolume.js";
7
- import { GPULightCollection } from "../light/GPULightCollection.js";
8
- import { GPUSceneVolumetrics } from "../volumetrics/GPUSceneVolumetrics.js";
9
- import { Scene } from "./Scene.js";
10
- import { GPUInstancesAccelerationStructure } from "./GPUInstancesAccelerationStructure.js";
11
- import { GPUMeshSkinningContext } from "../animation/skinning/GPUMeshSkinningContext.js";
12
- import {
13
- shader_refresh_all_mesh_bounds,
14
- WORKGROUP_SIZE as REFRESH_ALL_MESH_BOUNDS_WORKGROUP_SIZE
15
- } from "../object_property/shader_refresh_all_mesh_bounds.js";
16
- import { GPUEntityBoundsReadback } from "./rows/GPUEntityBoundsReadback.js";
17
- import { GPUSceneRows } from "./rows/GPUSceneRows.js";
18
- import { generate_record_writer } from "../buffer/table/serde/generate_record_writer.js";
19
- import { NODE3D_SCENE_ID, NODE3D_STRUCT } from "./NODE3D_STRUCT.js";
20
- import { SCENE_DATABASE_SPEC } from "./SCENE_DATABASE_SPEC.js";
21
-
22
- /**
23
- * @typedef {import("../GraphicsContext.js").GraphicsContext} GraphicsContext
24
- * @typedef {import("../geometry/GPUGeometryManager.js").GPUGeometryManager} GPUGeometryManager
25
- * @typedef {import("../material/GPUMaterialManager.js").GPUMaterialManager} GPUMaterialManager
26
- * @typedef {import("../material/ShadeMaterial.js").ShadeMaterial} ShadeMaterial
27
- * @typedef {import("../buffer/table/serde/generate_record_writer.js").GPURecordWriter} GPURecordWriter
28
- */
29
-
30
-
31
- let id_counter = 0;
32
-
33
- /**
34
- * Row 0 of the `transforms` table: the scene root. Identity, and its own parent — which is what the
35
- * hierarchy pass takes as "no further ancestor" — so an entity whose attachment names no entity
36
- * with a row is posed under it in world space, and the composition is exact. Written once, at the
37
- * first {@link GPUSceneContext.build}, so it goes up with the first rows rather than being left to
38
- * whatever the buffer held.
39
- */
40
- const ROOT_RECORD = {
41
- local_translation: new Float32Array([0, 0, 0]),
42
- local_rotation: new Float32Array([0, 0, 0, 1]),
43
- local_scale: new Float32Array([1, 1, 1]),
44
- global: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
45
- prev_global: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
46
- parent: NODE3D_SCENE_ID,
47
- };
48
-
49
- /**
50
- * @type {GPURecordWriter}
51
- */
52
- const ROOT_RECORD_WRITER = generate_record_writer(NODE3D_STRUCT);
53
-
54
- /**
55
- * What a scene with no rows draws with.
56
- *
57
- * @type {ShadeMaterial[]}
58
- */
59
- const NO_MATERIALS = Object.freeze([]);
60
-
61
- /**
62
- * The GPU side of one {@link Scene}: its database — the `transforms` and `meshes` tables, one row
63
- * per entity that is drawn or posed, at `entity + 1` — and what is built over that database each
64
- * frame: the lights' records, the probe volume, the volumetrics, the animation, the TLAS.
65
- *
66
- * Nothing here holds a hierarchy of its own. An entity's row names its parent's row and the GPU
67
- * composes the chain each frame, derives every mesh row's bounds under the composed matrix and
68
- * builds the tree over them — see {@link tick}. What the CPU writes is what it owns: an entity's
69
- * local TRS and the row of its parent, through {@link rows}.
70
- */
71
- export class GPUSceneContext {
72
-
73
- /**
74
- * @readonly
75
- * @type {number}
76
- */
77
- id = id_counter++;
78
-
79
- /**
80
- * @type {GPUGeometryManager}
81
- */
82
- geometries;
83
-
84
- /**
85
- * @type {GPUMaterialManager}
86
- */
87
- materials;
88
-
89
-
90
- /**
91
- * @type {Brick4LightMap}
92
- */
93
- volumetric_light_map;
94
-
95
-
96
- /**
97
- *
98
- * @type {Scene}
99
- */
100
- #scene;
101
-
102
- /**
103
- *
104
- * @return {Scene}
105
- */
106
- get scene() {
107
- return this.#scene;
108
- }
109
-
110
- /**
111
- * @type {GPUSceneVolumetrics}
112
- */
113
- #volumetrics;
114
-
115
- get volumetrics() {
116
- return this.#volumetrics;
117
- }
118
-
119
- /**
120
- * @type {GPULightCollection}
121
- */
122
- #lights;
123
-
124
-
125
- /**
126
- *
127
- * @returns {GPULightCollection}
128
- */
129
- get lights() {
130
- return this.#lights;
131
- }
132
-
133
- /**
134
- * @type {GPULightProbeVolume}
135
- */
136
- #light_probe_volume;
137
-
138
- /**
139
- *
140
- * @returns {GPULightProbeVolume}
141
- */
142
- get light_probe_volume() {
143
- return this.#light_probe_volume;
144
- }
145
-
146
- /**
147
- * @type {GPUInstancesAccelerationStructure}
148
- */
149
- #tlas
150
-
151
- /**
152
- *
153
- * @returns {GPUInstancesAccelerationStructure}
154
- */
155
- get tlas() {
156
- return this.#tlas;
157
- }
158
-
159
- /**
160
- * GPU-resident scene database — mesh metadata in the `meshes` table,
161
- * transforms in the `transforms` table, one row per entity in each.
162
- * Consumers bind `scene_database.buffer` as `array<u32>` and read
163
- * records via `scene_read_mesh` / `scene_read_node` (see
164
- * `chunk_scene_database_access`).
165
- *
166
- * @type {GPUDatabase}
167
- */
168
- #scene_database;
169
-
170
- /**
171
- * Entities as rows of {@link scene_database} — see {@link rows}. Built the first time it is
172
- * asked for, which needs the scene to name its dataset by then.
173
- *
174
- * @type {GPUSceneRows|null}
175
- */
176
- #rows = null;
177
-
178
- /**
179
- * Whether {@link ROOT_RECORD} has been written to row 0 — once, at the first build.
180
- *
181
- * @type {boolean}
182
- */
183
- #root_written = false;
184
-
185
- /**
186
- * Per-scene skinning state — clone-index cache today, plus
187
- * Skin-bindings and the per-frame skinning compute dispatch in
188
- * later phases. Lazily constructed because it depends on
189
- * {@link geometries}, which {@link SceneManager#create} assigns
190
- * externally after this constructor runs. See {@link skinning} for
191
- * access.
192
- *
193
- * @type {GPUMeshSkinningContext|null}
194
- */
195
- #skinning = null;
196
-
197
- /**
198
- * Per-scene animation system — clips, tracks, curves, skins, and
199
- * the skin-matrix scratch buffer. Owned here (rather than passed
200
- * in) so that consumers fetching `gpu_scene.animation_manager`
201
- * always see the same instance, and so per-frame database flush
202
- * can be wired into {@link update} alongside the rest of the
203
- * scene's bookkeeping.
204
- *
205
- * @type {GPUAnimationManager}
206
- */
207
- #animation_manager;
208
-
209
- /**
210
- *
211
- * @param {GraphicsContext} graphics
212
- * @param {Scene} scene
213
- */
214
- constructor(graphics, scene) {
215
- assert.defined(scene, 'scene');
216
- assert.isObject(scene, 'scene');
217
- assert.equal(scene.isScene, true, 'scene.isScene !== true');
218
-
219
- const device = graphics.device;
220
-
221
- this.#scene = scene;
222
- this.#device = device;
223
- this.#graphics = graphics;
224
-
225
- this.#tlas = new GPUInstancesAccelerationStructure(graphics);
226
-
227
- // Pass `this` so the light collection (and its child shadow context) can use
228
- // this scene's GPUSceneContext when building its own ViewManager — avoids
229
- // threading the renderer-level ViewManager / SceneManager through every layer.
230
- this.#lights = new GPULightCollection(graphics, this);
231
-
232
- this.#scene_database = new GPUDatabase({
233
- device,
234
- definition: SCENE_DATABASE_SPEC,
235
- label: "GPUSceneContext/Database",
236
- });
237
-
238
- this.#light_probe_volume = new GPULightProbeVolume(device, scene.light_probe_volume);
239
-
240
- this.volumetric_light_map = new Brick4LightMap(device);
241
-
242
- this.#volumetrics = new GPUSceneVolumetrics(device, scene.volumetrics);
243
-
244
- this.#animation_manager = new GPUAnimationManager(device, `GPUSceneContext[${this.id}]/Animation`, this);
245
- }
246
-
247
- /**
248
- * @returns {GPUAnimationManager}
249
- */
250
- get animation_manager() {
251
- return this.#animation_manager;
252
- }
253
-
254
- /**
255
- * @type {GPUDevice}
256
- */
257
- #device;
258
-
259
- /**
260
- * @type {GraphicsContext}
261
- */
262
- #graphics;
263
-
264
- /**
265
- *
266
- * @returns {GPUDatabase}
267
- */
268
- get scene_database() {
269
- return this.#scene_database;
270
- }
271
-
272
- /**
273
- * The underlying buffer that backs {@link scene_database} — bind this
274
- * as `array<u32>` in shaders that read mesh / transform records.
275
- *
276
- * @returns {GPUBuffer}
277
- */
278
- get scene_database_buffer() {
279
- return this.#scene_database.buffer;
280
- }
281
-
282
- /**
283
- * Per-scene skinning subsystem. Routes skinned rows through
284
- * per-instance geometry clones; later phases grow Skin-binding
285
- * management and the per-frame skinning compute onto it.
286
- *
287
- * Lazy because it depends on {@link geometries}, which is assigned
288
- * externally after this object's constructor returns
289
- * (see {@link SceneManager}). The first access happens during
290
- * {@link build}, which runs well after that assignment.
291
- *
292
- * @returns {GPUMeshSkinningContext}
293
- */
294
- get skinning() {
295
- if (this.#skinning === null) {
296
- this.#skinning = new GPUMeshSkinningContext({
297
- geometries: this.geometries,
298
- animation_manager: this.#animation_manager,
299
- scene_context: this,
300
- device: this.#device,
301
- });
302
- }
303
- return this.#skinning;
304
- }
305
-
306
- get gpu_memory_usage(){
307
- let r = 0;
308
-
309
- r+= this.#scene_database.gpu_memory_usage;
310
-
311
- r += this.#lights.gpu_memory_usage;
312
- r += this.#light_probe_volume.gpu_memory_usage;
313
- r += this.volumetric_light_map.gpu_memory_usage;
314
-
315
- return r;
316
- }
317
-
318
- /**
319
- * Entities as rows of the scene database, at `entity + 1` in both tables.
320
- *
321
- * A system that places an entity acquires its row here and marks it on the dataset's change
322
- * events; a scene with no systems places through `prefab_place` and `place_primitive`, which
323
- * do the same by hand; and {@link build} flushes what was marked. The scene has to name its
324
- * dataset ({@link Scene.dataset}) before this is first asked for, because the rows are written
325
- * from that dataset's components.
326
- *
327
- * @returns {GPUSceneRows}
328
- */
329
- get rows() {
330
- return this.#obtain_rows();
331
- }
332
-
333
- /**
334
- * An entity's world bounds as the GPU holds them, read back — what a consumer that wants the
335
- * box that is drawn asks, a frame or two behind the device. Built on demand, over the rows.
336
- *
337
- * @returns {GPUEntityBoundsReadback}
338
- */
339
- get bounds() {
340
- if (this.#bounds === null) {
341
- this.#bounds = new GPUEntityBoundsReadback(this.#scene_database.readback, this.rows);
342
- }
343
-
344
- return this.#bounds;
345
- }
346
-
347
- /**
348
- * @type {GPUEntityBoundsReadback|null}
349
- */
350
- #bounds = null;
351
-
352
- /**
353
- * Every material something in the scene draws with, each once — the rows' own list, kept
354
- * current as meshes attach and detach. What the rasterizers iterate to decide which buckets
355
- * and pipelines a frame needs. The array is reused until the set changes; a scene with no
356
- * rows draws with nothing.
357
- *
358
- * @returns {ShadeMaterial[]}
359
- */
360
- get drawn_materials() {
361
- return this.#rows === null ? NO_MATERIALS : this.#rows.materials;
362
- }
363
-
364
- /**
365
- * @returns {GPUSceneRows}
366
- */
367
- #obtain_rows() {
368
- if (this.#rows === null) {
369
- const dataset = this.#scene.dataset;
370
-
371
- assert.notNull(dataset, 'the scene names no dataset — set Scene.dataset before asking for its rows');
372
- assert.defined(dataset, 'the scene names no dataset — set Scene.dataset before asking for its rows');
373
-
374
- this.#rows = new GPUSceneRows({
375
- dataset,
376
- transforms: this.#scene_database.get('transforms'),
377
- meshes: this.#scene_database.get('meshes'),
378
- geometries: this.geometries,
379
- materials: this.materials,
380
- tlas: this.#tlas,
381
- skinning: () => this.skinning,
382
- });
383
- }
384
-
385
- return this.#rows;
386
- }
387
-
388
- /**
389
- * Bring the GPU scene database in line with the scene, writing only the rows that changed:
390
- * the scene root's, once; a light's, as it arrives; and every entity's row that a system
391
- * marked since the last build. Nothing is walked — the rows keep what was marked.
392
- */
393
- build() {
394
- let written = 0;
395
-
396
- if (!this.#root_written) {
397
- this.#scene_database.get('transforms').set_record(NODE3D_SCENE_ID, ROOT_RECORD, ROOT_RECORD_WRITER);
398
-
399
- this.#root_written = true;
400
- written++;
401
- }
402
-
403
- // Entities are rows — marked by the systems that place them, written here. Built on demand
404
- // because the scene may name its dataset after this context was made.
405
- if (this.#rows === null && this.#scene.dataset !== null && this.#scene.dataset !== undefined) {
406
- this.rows;
407
- }
408
-
409
- if (this.#rows !== null) {
410
- // A light is an entity's row, held here on the light collection's behalf — before the
411
- // flush, so a light that arrived this frame is written this frame.
412
- this.#lights.sync_rows(this.#rows);
413
-
414
- written += this.#rows.flush();
415
- } else {
416
- assert.equal(this.#scene.lights.elements.length, 0, 'the scene has lights and names no dataset; a light is an entity of one');
417
- }
418
-
419
- if (written === 0) {
420
- return;
421
- }
422
-
423
- // Flush the scene database — only the rows that changed carry an upload record.
424
- const cmd = ShadeGPUCommandContext.create(this.#graphics, 'GPUSceneContext/database-build');
425
-
426
- // Also flush the global geometry manager. The first time a scene
427
- // sees a new mesh, the rows' flush calls `geometries.add(...)`,
428
- // which registers the source MeshletGeometry and flips
429
- // `geometries.__needs_update = true`. The next per-frame
430
- // `graphics.update()` would normally rebuild
431
- // `geometries.buffer_metadata` to reflect those new entries — but
432
- // `graphics.update()` runs at the *head* of `Renderer.#begin_frame`,
433
- // *before* this scene.build, so the buffer_metadata stays whatever
434
- // it was at the start of the frame (often the zero-record placeholder
435
- // from the renderer's startup build, when the scene's geometries
436
- // hadn't been registered yet).
437
- //
438
- // Any pass reading `geometries[mesh.geometry]` later in the frame
439
- // (rasterization meshlet expansion, `shader_refresh_all_mesh_bounds`,
440
- // etc.) sees zeros — `meshlets_count = 0` produces no triangles and
441
- // `bounding_box = (0,0,0)-(0,0,0)` collapses world bounds to a
442
- // single point in `shader_refresh_all_mesh_bounds`, which then writes
443
- // those collapsed bounds back into `meshes_table`. Subsequent frames
444
- // get a correct `buffer_metadata` from the next `graphics.update()`,
445
- // but the `meshes_table` bounds stay collapsed until refresh_all
446
- // overwrites them again — and frustum cull on the collapsed bounds
447
- // can drop meshes whose collapsed point sits outside the frustum.
448
- //
449
- // Symptom: stress test rendered ground (whose collapsed point at the
450
- // origin survives the cull) but dropped most trees (whose collapsed
451
- // points scattered across the grid trigger cull edge cases).
452
- //
453
- // Calling `geometries.update(cmd)` here ensures the new buffer is in
454
- // place before any pass in the same frame reads it. Idempotent / no-op
455
- // when nothing's queued, so the per-frame `graphics.update` head call
456
- // and this build-time call together leave the buffer in the right
457
- // state under both lifetimes.
458
- this.geometries.update(cmd);
459
-
460
- this.#scene_database.update(cmd);
461
- cmd.finish();
462
- }
463
-
464
- /**
465
- *
466
- * @param {GraphicsContext} ctx
467
- */
468
- update(ctx) {
469
- assert.defined(ctx, 'ctx');
470
-
471
- // TODO add a fast update check, so that calling this multiple times per frame can be fast.
472
-
473
- // The apply point of the scene database's readback: whatever the device has answered since
474
- // — an entity's drawn bounds, asked for through {@link bounds} — is delivered before
475
- // anything this frame reads it, and never mid-update.
476
- this.#scene_database.readback.apply();
477
-
478
- this.#light_probe_volume.update();
479
- this.#lights.update(ctx);
480
- this.#volumetrics.update(ctx, 0);
481
-
482
- // Per-frame animation database flush — picks up any new clips,
483
- // tracks, skins, or per-frame `set_time` writes queued since
484
- // last frame. Idempotent / cheap when nothing's queued.
485
- const animation_cmd = ShadeGPUCommandContext.create(this.#graphics, 'GPUSceneContext/animation-flush');
486
- this.#animation_manager.update(animation_cmd);
487
- animation_cmd.finish();
488
-
489
- // TODO update SDF
490
-
491
- this.build();
492
- }
493
-
494
- /**
495
- * Per-frame GPU-side scene update, once per scene per frame, after every view's {@link update}
496
- * has flushed what the CPU changed:
497
- *
498
- * 1. the animation tick — pending registrations drained, `clip.time` advanced, apply → resolve
499
- * → hierarchy propagate → skin-matrix prep → skinning, through {@link GPUAnimationManager#tick};
500
- * 2. render bounds — every mesh row's world box and sphere, derived from its geometry under
501
- * the `global` the hierarchy pass just composed, because the CPU does not hold that matrix
502
- * for a subtree the GPU drives;
503
- * 3. light poses — every light record's position and direction, from its entity's row, for
504
- * the same reason;
505
- * 4. the TLAS — rebuilt from those bounds when anything moved, on either side.
506
- *
507
- * All four into `cmd_ctx`, in that order, so the tree the frame's ray passes traverse is built
508
- * over this frame's bounds rather than last frame's, and what lights the frame is where it is
509
- * this frame.
510
- *
511
- * `dt_seconds` is wall-clock seconds since the last call (the renderer's `time_delta_seconds`
512
- * argument to `render()`).
513
- *
514
- * @param {ShadeGPUCommandContext} cmd_ctx
515
- * @param {number} dt_seconds wall-clock seconds since last call
516
- */
517
- tick(cmd_ctx, dt_seconds) {
518
- const animated = this.#animation_manager.tick(cmd_ctx, dt_seconds);
519
-
520
- this.#refresh_mesh_bounds(cmd_ctx);
521
-
522
- this.#lights.refresh_poses(cmd_ctx, this.scene_database_buffer);
523
-
524
- if (animated) {
525
- // Rows moved on the GPU, where no version was bumped: a clip resolved into a TRS, a
526
- // skinning pass moved a clone's vertices. The bounds pass just re-derived their boxes,
527
- // and the tree is built over boxes.
528
- this.#tlas.invalidate();
529
- }
530
-
531
- this.#tlas.update(this.scene_database_buffer, cmd_ctx);
532
-
533
- // What was asked of the scene database this frame is copied out at the tail of the
534
- // database's own update when rows were written this frame, and here — after the bounds
535
- // pass — when none were; either way what lands, at the head of a later frame's update, is
536
- // the box the GPU held when the copy ran.
537
- this.#scene_database.readback.encode(cmd_ctx);
538
- }
539
-
540
- /**
541
- * Recompute every mesh row's world bounds from its geometry under its composed `global`.
542
- * Skipped when there is no mesh row; otherwise one thread per slot of the `meshes` table's
543
- * pages, occupancy-masked.
544
- *
545
- * @param {ShadeGPUCommandContext} cmd_ctx
546
- */
547
- #refresh_mesh_bounds(cmd_ctx) {
548
- const meshes_table = this.#scene_database.get('meshes');
549
-
550
- const group_count = meshes_table.dispatch_group_count(REFRESH_ALL_MESH_BOUNDS_WORKGROUP_SIZE);
551
-
552
- if (group_count === 0) {
553
- return;
554
- }
555
-
556
- const geometries = this.geometries.buffer_metadata;
557
-
558
- if (geometries === null) {
559
- // a mesh row names a geometry, and a geometry is registered before its row is written
560
- // — so this is a scene whose first frame has not flushed the geometry manager yet
561
- return;
562
- }
563
-
564
- shader_refresh_all_mesh_bounds.dispatch({
565
- encoder: cmd_ctx,
566
- group_count_x: group_count,
567
- bindings: {
568
- scene_database: this.scene_database_buffer,
569
- geometries,
570
- },
571
- });
572
- }
573
-
574
- destroy() {
575
- if (this.#rows !== null) {
576
- this.#rows.destroy();
577
- }
578
-
579
- this.#scene_database.destroy();
580
- this.#lights.destroy();
581
- this.#light_probe_volume.destroy();
582
- this.#animation_manager.destroy();
583
- if (this.#skinning !== null) {
584
- this.#skinning.destroy();
585
- }
586
- }
587
-
588
- }
589
-
590
- /**
591
- * @readonly
592
- * @type {boolean}
593
- */
594
- GPUSceneContext.prototype.isGPUSceneContext = true;
1
+ import { GPUVirtualGeometryPool } from "../geometry/virtual/runtime/GPUVirtualGeometryPool.js";
2
+ import { GPUVirtualGeometryRuntime } from "../geometry/virtual/runtime/GPUVirtualGeometryRuntime.js";
3
+ import { assert } from "../../../core/assert.js";
4
+ import { ShadeGPUCommandContext } from "../../device/ShadeGPUCommandContext.js";
5
+ import { GPUAnimationManager } from "../animation/GPUAnimationManager.js";
6
+ import { GPUDatabase } from "../buffer/table/GPUDatabase.js";
7
+ import { Brick4LightMap } from "../global_illumination/brick4/Brick4LightMap.js";
8
+ import { GPULightProbeVolume } from "../global_illumination/lpv/GPULightProbeVolume.js";
9
+ import { GPULightCollection } from "../light/GPULightCollection.js";
10
+ import { GPUSceneVolumetrics } from "../volumetrics/GPUSceneVolumetrics.js";
11
+ import { Scene } from "./Scene.js";
12
+ import { GPUInstancesAccelerationStructure } from "./GPUInstancesAccelerationStructure.js";
13
+ import { GPUMeshSkinningContext } from "../animation/skinning/GPUMeshSkinningContext.js";
14
+ import {
15
+ shader_refresh_all_mesh_bounds,
16
+ WORKGROUP_SIZE as REFRESH_ALL_MESH_BOUNDS_WORKGROUP_SIZE
17
+ } from "../object_property/shader_refresh_all_mesh_bounds.js";
18
+ import { GPUEntityBoundsReadback } from "./rows/GPUEntityBoundsReadback.js";
19
+ import { GPUSceneRows } from "./rows/GPUSceneRows.js";
20
+ import { generate_record_writer } from "../buffer/table/serde/generate_record_writer.js";
21
+ import { NODE3D_SCENE_ID, NODE3D_STRUCT } from "./NODE3D_STRUCT.js";
22
+ import { SCENE_DATABASE_SPEC } from "./SCENE_DATABASE_SPEC.js";
23
+
24
+ /**
25
+ * @typedef {import("../GraphicsContext.js").GraphicsContext} GraphicsContext
26
+ * @typedef {import("../geometry/GPUGeometryManager.js").GPUGeometryManager} GPUGeometryManager
27
+ * @typedef {import("../material/GPUMaterialManager.js").GPUMaterialManager} GPUMaterialManager
28
+ * @typedef {import("../material/ShadeMaterial.js").ShadeMaterial} ShadeMaterial
29
+ * @typedef {import("../buffer/table/serde/generate_record_writer.js").GPURecordWriter} GPURecordWriter
30
+ */
31
+
32
+
33
+ let id_counter = 0;
34
+
35
+ /**
36
+ * Row 0 of the `transforms` table: the scene root. Identity, and its own parent — which is what the
37
+ * hierarchy pass takes as "no further ancestor" — so an entity whose attachment names no entity
38
+ * with a row is posed under it in world space, and the composition is exact. Written once, at the
39
+ * first {@link GPUSceneContext.build}, so it goes up with the first rows rather than being left to
40
+ * whatever the buffer held.
41
+ */
42
+ const ROOT_RECORD = {
43
+ local_translation: new Float32Array([0, 0, 0]),
44
+ local_rotation: new Float32Array([0, 0, 0, 1]),
45
+ local_scale: new Float32Array([1, 1, 1]),
46
+ global: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
47
+ prev_global: new Float32Array([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]),
48
+ parent: NODE3D_SCENE_ID,
49
+ };
50
+
51
+ /**
52
+ * @type {GPURecordWriter}
53
+ */
54
+ const ROOT_RECORD_WRITER = generate_record_writer(NODE3D_STRUCT);
55
+
56
+ /**
57
+ * What a scene with no rows draws with.
58
+ *
59
+ * @type {ShadeMaterial[]}
60
+ */
61
+ const NO_MATERIALS = Object.freeze([]);
62
+
63
+ /**
64
+ * The GPU side of one {@link Scene}: its database — the `transforms` and `meshes` tables, one row
65
+ * per entity that is drawn or posed, at `entity + 1` — and what is built over that database each
66
+ * frame: the lights' records, the probe volume, the volumetrics, the animation, the TLAS.
67
+ *
68
+ * Nothing here holds a hierarchy of its own. An entity's row names its parent's row and the GPU
69
+ * composes the chain each frame, derives every mesh row's bounds under the composed matrix and
70
+ * builds the tree over them — see {@link tick}. What the CPU writes is what it owns: an entity's
71
+ * local TRS and the row of its parent, through {@link rows}.
72
+ */
73
+ export class GPUSceneContext {
74
+
75
+ /**
76
+ * @readonly
77
+ * @type {number}
78
+ */
79
+ id = id_counter++;
80
+
81
+ /**
82
+ * @type {GPUGeometryManager}
83
+ */
84
+ geometries;
85
+
86
+ /**
87
+ * @type {GPUMaterialManager}
88
+ */
89
+ materials;
90
+
91
+
92
+ /**
93
+ * @type {Brick4LightMap}
94
+ */
95
+ volumetric_light_map;
96
+
97
+
98
+ /**
99
+ *
100
+ * @type {Scene}
101
+ */
102
+ #scene;
103
+
104
+ /**
105
+ *
106
+ * @return {Scene}
107
+ */
108
+ get scene() {
109
+ return this.#scene;
110
+ }
111
+
112
+ /**
113
+ * @type {GPUSceneVolumetrics}
114
+ */
115
+ #volumetrics;
116
+
117
+ get volumetrics() {
118
+ return this.#volumetrics;
119
+ }
120
+
121
+ /**
122
+ * @type {GPULightCollection}
123
+ */
124
+ #lights;
125
+
126
+
127
+ /**
128
+ *
129
+ * @returns {GPULightCollection}
130
+ */
131
+ get lights() {
132
+ return this.#lights;
133
+ }
134
+
135
+ /**
136
+ * @type {GPULightProbeVolume}
137
+ */
138
+ #light_probe_volume;
139
+
140
+ /**
141
+ *
142
+ * @returns {GPULightProbeVolume}
143
+ */
144
+ get light_probe_volume() {
145
+ return this.#light_probe_volume;
146
+ }
147
+
148
+ /**
149
+ * @type {GPUInstancesAccelerationStructure}
150
+ */
151
+ #tlas
152
+
153
+ /**
154
+ *
155
+ * @returns {GPUInstancesAccelerationStructure}
156
+ */
157
+ get tlas() {
158
+ return this.#tlas;
159
+ }
160
+
161
+ /**
162
+ * GPU-resident scene database — mesh metadata in the `meshes` table,
163
+ * transforms in the `transforms` table, one row per entity in each.
164
+ * Consumers bind `scene_database.buffer` as `array<u32>` and read
165
+ * records via `scene_read_mesh` / `scene_read_node` (see
166
+ * `chunk_scene_database_access`).
167
+ *
168
+ * @type {GPUDatabase}
169
+ */
170
+ #scene_database;
171
+
172
+ /**
173
+ * Entities as rows of {@link scene_database} — see {@link rows}. Built the first time it is
174
+ * asked for, which needs the scene to name its dataset by then.
175
+ *
176
+ * @type {GPUSceneRows|null}
177
+ */
178
+ #rows = null;
179
+
180
+ /**
181
+ * Whether {@link ROOT_RECORD} has been written to row 0 — once, at the first build.
182
+ *
183
+ * @type {boolean}
184
+ */
185
+ #root_written = false;
186
+
187
+ /**
188
+ * Per-scene skinning state — clone-index cache today, plus
189
+ * Skin-bindings and the per-frame skinning compute dispatch in
190
+ * later phases. Lazily constructed because it depends on
191
+ * {@link geometries}, which {@link SceneManager#create} assigns
192
+ * externally after this constructor runs. See {@link skinning} for
193
+ * access.
194
+ *
195
+ * @type {GPUMeshSkinningContext|null}
196
+ */
197
+ #skinning = null;
198
+
199
+ /**
200
+ * Per-scene animation system — clips, tracks, curves, skins, and
201
+ * the skin-matrix scratch buffer. Owned here (rather than passed
202
+ * in) so that consumers fetching `gpu_scene.animation_manager`
203
+ * always see the same instance, and so per-frame database flush
204
+ * can be wired into {@link update} alongside the rest of the
205
+ * scene's bookkeeping.
206
+ *
207
+ * @type {GPUAnimationManager}
208
+ */
209
+ #animation_manager;
210
+
211
+ /**
212
+ *
213
+ * @param {GraphicsContext} graphics
214
+ * @param {Scene} scene
215
+ */
216
+ constructor(graphics, scene) {
217
+ assert.defined(scene, 'scene');
218
+ assert.isObject(scene, 'scene');
219
+ assert.equal(scene.isScene, true, 'scene.isScene !== true');
220
+
221
+ const device = graphics.device;
222
+
223
+ this.#scene = scene;
224
+ this.#device = device;
225
+ this.#graphics = graphics;
226
+
227
+ this.#tlas = new GPUInstancesAccelerationStructure(graphics);
228
+
229
+ // Pass `this` so the light collection (and its child shadow context) can use
230
+ // this scene's GPUSceneContext when building its own ViewManager — avoids
231
+ // threading the renderer-level ViewManager / SceneManager through every layer.
232
+ this.#lights = new GPULightCollection(graphics, this);
233
+
234
+ this.#scene_database = new GPUDatabase({
235
+ device,
236
+ definition: SCENE_DATABASE_SPEC,
237
+ label: "GPUSceneContext/Database",
238
+ });
239
+
240
+ this.#light_probe_volume = new GPULightProbeVolume(device, scene.light_probe_volume);
241
+
242
+ this.volumetric_light_map = new Brick4LightMap(device);
243
+
244
+ this.#volumetrics = new GPUSceneVolumetrics(device, scene.volumetrics);
245
+
246
+ this.#animation_manager = new GPUAnimationManager(device, `GPUSceneContext[${this.id}]/Animation`, this);
247
+ }
248
+
249
+ /**
250
+ * The virtual-geometry runtime, once something has asked for one.
251
+ *
252
+ * `undefined` until {@link enable_virtual_geometry}, and every rasterization path treats that
253
+ * as "there is no virtual geometry in this scene" and skips the cut entirely. Opt-in rather
254
+ * than always-on because the pool owns real buffers — a page arena, a page table, a per-asset
255
+ * lookup — and a scene of ordinary meshes should not pay for them.
256
+ *
257
+ * @type {GPUVirtualGeometryRuntime|undefined}
258
+ */
259
+ #virtual_geometry;
260
+
261
+ /**
262
+ * @returns {GPUVirtualGeometryRuntime|undefined}
263
+ */
264
+ get virtual_geometry() {
265
+ return this.#virtual_geometry;
266
+ }
267
+
268
+ /**
269
+ * Give this scene a virtual-geometry runtime, sharing the meshlet pool with everything else it
270
+ * draws.
271
+ *
272
+ * Sharing the pool is the whole seam on this side: a cut emits `MESHLET_DEFINITION_STRUCT`
273
+ * records naming rows of the same metadata buffer a static mesh's meshlets live in, so nothing
274
+ * downstream of the expansion has to know which kind of geometry it is looking at.
275
+ *
276
+ * @param {Object} [options] passed to {@link GPUVirtualGeometryPool} and
277
+ * {@link GPUVirtualGeometryRuntime}
278
+ * @returns {GPUVirtualGeometryRuntime}
279
+ */
280
+ enable_virtual_geometry(options = {}) {
281
+ if (this.#virtual_geometry !== undefined) {
282
+ return this.#virtual_geometry;
283
+ }
284
+
285
+ const pool = new GPUVirtualGeometryPool({
286
+ device: this.#device,
287
+ meshlets: this.geometries.meshlets,
288
+ ...options,
289
+ });
290
+
291
+ this.#virtual_geometry = new GPUVirtualGeometryRuntime({
292
+ pool,
293
+ device: this.#device,
294
+ ...options,
295
+ });
296
+
297
+ return this.#virtual_geometry;
298
+ }
299
+
300
+ /**
301
+ * @returns {GPUAnimationManager}
302
+ */
303
+ get animation_manager() {
304
+ return this.#animation_manager;
305
+ }
306
+
307
+ /**
308
+ * @type {GPUDevice}
309
+ */
310
+ #device;
311
+
312
+ /**
313
+ * @type {GraphicsContext}
314
+ */
315
+ #graphics;
316
+
317
+ /**
318
+ *
319
+ * @returns {GPUDatabase}
320
+ */
321
+ get scene_database() {
322
+ return this.#scene_database;
323
+ }
324
+
325
+ /**
326
+ * The underlying buffer that backs {@link scene_database} — bind this
327
+ * as `array<u32>` in shaders that read mesh / transform records.
328
+ *
329
+ * @returns {GPUBuffer}
330
+ */
331
+ get scene_database_buffer() {
332
+ return this.#scene_database.buffer;
333
+ }
334
+
335
+ /**
336
+ * Per-scene skinning subsystem. Routes skinned rows through
337
+ * per-instance geometry clones; later phases grow Skin-binding
338
+ * management and the per-frame skinning compute onto it.
339
+ *
340
+ * Lazy because it depends on {@link geometries}, which is assigned
341
+ * externally after this object's constructor returns
342
+ * (see {@link SceneManager}). The first access happens during
343
+ * {@link build}, which runs well after that assignment.
344
+ *
345
+ * @returns {GPUMeshSkinningContext}
346
+ */
347
+ get skinning() {
348
+ if (this.#skinning === null) {
349
+ this.#skinning = new GPUMeshSkinningContext({
350
+ geometries: this.geometries,
351
+ animation_manager: this.#animation_manager,
352
+ scene_context: this,
353
+ device: this.#device,
354
+ });
355
+ }
356
+ return this.#skinning;
357
+ }
358
+
359
+ get gpu_memory_usage(){
360
+ let r = 0;
361
+
362
+ r+= this.#scene_database.gpu_memory_usage;
363
+
364
+ r += this.#lights.gpu_memory_usage;
365
+ r += this.#light_probe_volume.gpu_memory_usage;
366
+ r += this.volumetric_light_map.gpu_memory_usage;
367
+
368
+ return r;
369
+ }
370
+
371
+ /**
372
+ * Entities as rows of the scene database, at `entity + 1` in both tables.
373
+ *
374
+ * A system that places an entity acquires its row here and marks it on the dataset's change
375
+ * events; a scene with no systems places through `prefab_place` and `place_primitive`, which
376
+ * do the same by hand; and {@link build} flushes what was marked. The scene has to name its
377
+ * dataset ({@link Scene.dataset}) before this is first asked for, because the rows are written
378
+ * from that dataset's components.
379
+ *
380
+ * @returns {GPUSceneRows}
381
+ */
382
+ get rows() {
383
+ return this.#obtain_rows();
384
+ }
385
+
386
+ /**
387
+ * An entity's world bounds as the GPU holds them, read back — what a consumer that wants the
388
+ * box that is drawn asks, a frame or two behind the device. Built on demand, over the rows.
389
+ *
390
+ * @returns {GPUEntityBoundsReadback}
391
+ */
392
+ get bounds() {
393
+ if (this.#bounds === null) {
394
+ this.#bounds = new GPUEntityBoundsReadback(this.#scene_database.readback, this.rows);
395
+ }
396
+
397
+ return this.#bounds;
398
+ }
399
+
400
+ /**
401
+ * @type {GPUEntityBoundsReadback|null}
402
+ */
403
+ #bounds = null;
404
+
405
+ /**
406
+ * Every material something in the scene draws with, each once — the rows' own list, kept
407
+ * current as meshes attach and detach. What the rasterizers iterate to decide which buckets
408
+ * and pipelines a frame needs. The array is reused until the set changes; a scene with no
409
+ * rows draws with nothing.
410
+ *
411
+ * @returns {ShadeMaterial[]}
412
+ */
413
+ get drawn_materials() {
414
+ return this.#rows === null ? NO_MATERIALS : this.#rows.materials;
415
+ }
416
+
417
+ /**
418
+ * @returns {GPUSceneRows}
419
+ */
420
+ #obtain_rows() {
421
+ if (this.#rows === null) {
422
+ const dataset = this.#scene.dataset;
423
+
424
+ assert.notNull(dataset, 'the scene names no dataset — set Scene.dataset before asking for its rows');
425
+ assert.defined(dataset, 'the scene names no dataset — set Scene.dataset before asking for its rows');
426
+
427
+ this.#rows = new GPUSceneRows({
428
+ dataset,
429
+ transforms: this.#scene_database.get('transforms'),
430
+ meshes: this.#scene_database.get('meshes'),
431
+ geometries: this.geometries,
432
+ materials: this.materials,
433
+ tlas: this.#tlas,
434
+ skinning: () => this.skinning,
435
+ });
436
+ }
437
+
438
+ return this.#rows;
439
+ }
440
+
441
+ /**
442
+ * Bring the GPU scene database in line with the scene, writing only the rows that changed:
443
+ * the scene root's, once; a light's, as it arrives; and every entity's row that a system
444
+ * marked since the last build. Nothing is walked — the rows keep what was marked.
445
+ */
446
+ build() {
447
+ let written = 0;
448
+
449
+ if (!this.#root_written) {
450
+ this.#scene_database.get('transforms').set_record(NODE3D_SCENE_ID, ROOT_RECORD, ROOT_RECORD_WRITER);
451
+
452
+ this.#root_written = true;
453
+ written++;
454
+ }
455
+
456
+ // Entities are rows — marked by the systems that place them, written here. Built on demand
457
+ // because the scene may name its dataset after this context was made.
458
+ if (this.#rows === null && this.#scene.dataset !== null && this.#scene.dataset !== undefined) {
459
+ this.rows;
460
+ }
461
+
462
+ if (this.#rows !== null) {
463
+ // A light is an entity's row, held here on the light collection's behalf — before the
464
+ // flush, so a light that arrived this frame is written this frame.
465
+ this.#lights.sync_rows(this.#rows);
466
+
467
+ written += this.#rows.flush();
468
+ } else {
469
+ assert.equal(this.#scene.lights.elements.length, 0, 'the scene has lights and names no dataset; a light is an entity of one');
470
+ }
471
+
472
+ if (written === 0) {
473
+ return;
474
+ }
475
+
476
+ // Flush the scene database — only the rows that changed carry an upload record.
477
+ const cmd = ShadeGPUCommandContext.create(this.#graphics, 'GPUSceneContext/database-build');
478
+
479
+ // Also flush the global geometry manager. The first time a scene
480
+ // sees a new mesh, the rows' flush calls `geometries.add(...)`,
481
+ // which registers the source MeshletGeometry and flips
482
+ // `geometries.__needs_update = true`. The next per-frame
483
+ // `graphics.update()` would normally rebuild
484
+ // `geometries.buffer_metadata` to reflect those new entries — but
485
+ // `graphics.update()` runs at the *head* of `Renderer.#begin_frame`,
486
+ // *before* this scene.build, so the buffer_metadata stays whatever
487
+ // it was at the start of the frame (often the zero-record placeholder
488
+ // from the renderer's startup build, when the scene's geometries
489
+ // hadn't been registered yet).
490
+ //
491
+ // Any pass reading `geometries[mesh.geometry]` later in the frame
492
+ // (rasterization meshlet expansion, `shader_refresh_all_mesh_bounds`,
493
+ // etc.) sees zeros — `meshlets_count = 0` produces no triangles and
494
+ // `bounding_box = (0,0,0)-(0,0,0)` collapses world bounds to a
495
+ // single point in `shader_refresh_all_mesh_bounds`, which then writes
496
+ // those collapsed bounds back into `meshes_table`. Subsequent frames
497
+ // get a correct `buffer_metadata` from the next `graphics.update()`,
498
+ // but the `meshes_table` bounds stay collapsed until refresh_all
499
+ // overwrites them again — and frustum cull on the collapsed bounds
500
+ // can drop meshes whose collapsed point sits outside the frustum.
501
+ //
502
+ // Symptom: stress test rendered ground (whose collapsed point at the
503
+ // origin survives the cull) but dropped most trees (whose collapsed
504
+ // points scattered across the grid trigger cull edge cases).
505
+ //
506
+ // Calling `geometries.update(cmd)` here ensures the new buffer is in
507
+ // place before any pass in the same frame reads it. Idempotent / no-op
508
+ // when nothing's queued, so the per-frame `graphics.update` head call
509
+ // and this build-time call together leave the buffer in the right
510
+ // state under both lifetimes.
511
+ this.geometries.update(cmd);
512
+
513
+ this.#scene_database.update(cmd);
514
+ cmd.finish();
515
+ }
516
+
517
+ /**
518
+ *
519
+ * @param {GraphicsContext} ctx
520
+ */
521
+ update(ctx) {
522
+ assert.defined(ctx, 'ctx');
523
+
524
+ // TODO add a fast update check, so that calling this multiple times per frame can be fast.
525
+
526
+ // The apply point of the scene database's readback: whatever the device has answered since
527
+ // — an entity's drawn bounds, asked for through {@link bounds} — is delivered before
528
+ // anything this frame reads it, and never mid-update.
529
+ this.#scene_database.readback.apply();
530
+
531
+ this.#light_probe_volume.update();
532
+ this.#lights.update(ctx);
533
+ this.#volumetrics.update(ctx, 0);
534
+
535
+ // Per-frame animation database flush — picks up any new clips,
536
+ // tracks, skins, or per-frame `set_time` writes queued since
537
+ // last frame. Idempotent / cheap when nothing's queued.
538
+ const animation_cmd = ShadeGPUCommandContext.create(this.#graphics, 'GPUSceneContext/animation-flush');
539
+ this.#animation_manager.update(animation_cmd);
540
+ animation_cmd.finish();
541
+
542
+ // TODO update SDF
543
+
544
+ this.build();
545
+ }
546
+
547
+ /**
548
+ * Per-frame GPU-side scene update, once per scene per frame, after every view's {@link update}
549
+ * has flushed what the CPU changed:
550
+ *
551
+ * 1. the animation tick — pending registrations drained, `clip.time` advanced, apply → resolve
552
+ * → hierarchy propagate → skin-matrix prep → skinning, through {@link GPUAnimationManager#tick};
553
+ * 2. render bounds — every mesh row's world box and sphere, derived from its geometry under
554
+ * the `global` the hierarchy pass just composed, because the CPU does not hold that matrix
555
+ * for a subtree the GPU drives;
556
+ * 3. light poses — every light record's position and direction, from its entity's row, for
557
+ * the same reason;
558
+ * 4. the TLAS — rebuilt from those bounds when anything moved, on either side.
559
+ *
560
+ * All four into `cmd_ctx`, in that order, so the tree the frame's ray passes traverse is built
561
+ * over this frame's bounds rather than last frame's, and what lights the frame is where it is
562
+ * this frame.
563
+ *
564
+ * `dt_seconds` is wall-clock seconds since the last call (the renderer's `time_delta_seconds`
565
+ * argument to `render()`).
566
+ *
567
+ * @param {ShadeGPUCommandContext} cmd_ctx
568
+ * @param {number} dt_seconds wall-clock seconds since last call
569
+ */
570
+ tick(cmd_ctx, dt_seconds) {
571
+ const animated = this.#animation_manager.tick(cmd_ctx, dt_seconds);
572
+
573
+ this.#refresh_mesh_bounds(cmd_ctx);
574
+
575
+ this.#lights.refresh_poses(cmd_ctx, this.scene_database_buffer);
576
+
577
+ if (animated) {
578
+ // Rows moved on the GPU, where no version was bumped: a clip resolved into a TRS, a
579
+ // skinning pass moved a clone's vertices. The bounds pass just re-derived their boxes,
580
+ // and the tree is built over boxes.
581
+ this.#tlas.invalidate();
582
+ }
583
+
584
+ this.#tlas.update(this.scene_database_buffer, cmd_ctx);
585
+
586
+ // What was asked of the scene database this frame is copied out at the tail of the
587
+ // database's own update when rows were written this frame, and here — after the bounds
588
+ // pass — when none were; either way what lands, at the head of a later frame's update, is
589
+ // the box the GPU held when the copy ran.
590
+ this.#scene_database.readback.encode(cmd_ctx);
591
+ }
592
+
593
+ /**
594
+ * Recompute every mesh row's world bounds from its geometry under its composed `global`.
595
+ * Skipped when there is no mesh row; otherwise one thread per slot of the `meshes` table's
596
+ * pages, occupancy-masked.
597
+ *
598
+ * @param {ShadeGPUCommandContext} cmd_ctx
599
+ */
600
+ #refresh_mesh_bounds(cmd_ctx) {
601
+ const meshes_table = this.#scene_database.get('meshes');
602
+
603
+ const group_count = meshes_table.dispatch_group_count(REFRESH_ALL_MESH_BOUNDS_WORKGROUP_SIZE);
604
+
605
+ if (group_count === 0) {
606
+ return;
607
+ }
608
+
609
+ const geometries = this.geometries.buffer_metadata;
610
+
611
+ if (geometries === null) {
612
+ // a mesh row names a geometry, and a geometry is registered before its row is written
613
+ // — so this is a scene whose first frame has not flushed the geometry manager yet
614
+ return;
615
+ }
616
+
617
+ shader_refresh_all_mesh_bounds.dispatch({
618
+ encoder: cmd_ctx,
619
+ group_count_x: group_count,
620
+ bindings: {
621
+ scene_database: this.scene_database_buffer,
622
+ geometries,
623
+ },
624
+ });
625
+ }
626
+
627
+ destroy() {
628
+ if (this.#rows !== null) {
629
+ this.#rows.destroy();
630
+ }
631
+
632
+ this.#scene_database.destroy();
633
+ this.#lights.destroy();
634
+ this.#light_probe_volume.destroy();
635
+ this.#animation_manager.destroy();
636
+ if (this.#skinning !== null) {
637
+ this.#skinning.destroy();
638
+ }
639
+ }
640
+
641
+ }
642
+
643
+ /**
644
+ * @readonly
645
+ * @type {boolean}
646
+ */
647
+ GPUSceneContext.prototype.isGPUSceneContext = true;