@woosh/meep-engine 3.21.0 → 3.23.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 (1803) hide show
  1. package/README.md +96 -213
  2. package/editor/particles/effect/ParticleEffectDocument.d.ts.map +1 -1
  3. package/editor/particles/effect/ParticleEffectDocument.js +0 -3
  4. package/editor/particles/effect/ParticleReferenceSimulation.js +1 -1
  5. package/editor/persistence/SceneMetadata.d.ts +9 -4
  6. package/editor/persistence/SceneMetadata.d.ts.map +1 -1
  7. package/editor/persistence/SceneMetadata.js +4 -3
  8. package/editor/tools/TransformToolV2.d.ts +2 -1
  9. package/editor/tools/TransformToolV2.d.ts.map +1 -1
  10. package/editor/tools/engine/Tool.d.ts +1 -1
  11. package/editor/tools/engine/Tool.d.ts.map +1 -1
  12. package/editor/tools/engine/ToolEngine.d.ts +1 -1
  13. package/editor/tools/engine/ToolEngine.d.ts.map +1 -1
  14. package/editor/view/ecs/EntityEditor.d.ts +3 -2
  15. package/editor/view/ecs/EntityEditor.d.ts.map +1 -1
  16. package/editor/view/ecs/EntityList.d.ts +7 -3
  17. package/editor/view/ecs/EntityList.d.ts.map +1 -1
  18. package/editor/view/ecs/EntityList.js +3 -2
  19. package/editor/view/ecs/components/common/NumericIntervalController.d.ts +8 -3
  20. package/editor/view/ecs/components/common/NumericIntervalController.d.ts.map +1 -1
  21. package/editor/view/ecs/components/common/NumericIntervalController.js +3 -2
  22. package/editor/view/ecs/components/common/TextController.d.ts +7 -3
  23. package/editor/view/ecs/components/common/TextController.d.ts.map +1 -1
  24. package/editor/view/ecs/components/common/TextController.js +3 -2
  25. package/editor/view/library/MeshLibraryView.d.ts.map +1 -1
  26. package/editor/view/library/MeshLibraryView.js +3 -2
  27. package/editor/view/node-graph/NodeIconView.d.ts +5 -2
  28. package/editor/view/node-graph/NodeIconView.d.ts.map +1 -1
  29. package/editor/view/node-graph/NodeIconView.js +2 -1
  30. package/editor/view/node-graph/PortView.d.ts +9 -4
  31. package/editor/view/node-graph/PortView.d.ts.map +1 -1
  32. package/editor/view/node-graph/PortView.js +4 -3
  33. package/editor/view/particles/ColorLutGradientView.d.ts +7 -3
  34. package/editor/view/particles/ColorLutGradientView.d.ts.map +1 -1
  35. package/editor/view/particles/ColorLutGradientView.js +3 -2
  36. package/editor/view/particles/ScalarLutCurveView.d.ts +9 -4
  37. package/editor/view/particles/ScalarLutCurveView.d.ts.map +1 -1
  38. package/editor/view/particles/ScalarLutCurveView.js +4 -3
  39. package/editor/view/particles/effect/ParticleEmitterInspectorView.d.ts.map +1 -1
  40. package/editor/view/particles/effect/ParticleEmitterInspectorView.js +0 -1
  41. package/editor/view/tools/ToolView.d.ts +7 -3
  42. package/editor/view/tools/ToolView.d.ts.map +1 -1
  43. package/editor/view/tools/ToolView.js +3 -2
  44. package/package.json +2 -1
  45. package/src/core/binary/allocator/OffsetAllocator.d.ts +24 -1
  46. package/src/core/binary/allocator/OffsetAllocator.d.ts.map +1 -1
  47. package/src/core/binary/allocator/OffsetAllocator.js +101 -1
  48. package/src/core/binary/type/Field.d.ts +5 -1
  49. package/src/core/binary/type/Field.d.ts.map +1 -1
  50. package/src/core/binary/type/Field.js +4 -0
  51. package/src/core/binary/type/Type.d.ts +4 -0
  52. package/src/core/binary/type/Type.d.ts.map +1 -1
  53. package/src/core/binary/type/Type.js +4 -0
  54. package/src/core/cache/Cache.d.ts +15 -7
  55. package/src/core/cache/Cache.d.ts.map +1 -1
  56. package/src/core/cache/Cache.js +7 -6
  57. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.d.ts +5 -2
  58. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.d.ts.map +1 -1
  59. package/src/core/cache/wtinylfu/CacheRegionWTinylfu.js +2 -1
  60. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts +20 -10
  61. package/src/core/cache/wtinylfu/CacheWTinylfu.d.ts.map +1 -1
  62. package/src/core/cache/wtinylfu/CacheWTinylfu.js +9 -8
  63. package/src/core/collection/CuckooFilter.d.ts +8 -4
  64. package/src/core/collection/CuckooFilter.d.ts.map +1 -1
  65. package/src/core/collection/CuckooFilter.js +3 -2
  66. package/src/core/collection/list/List.d.ts +0 -1
  67. package/src/core/collection/list/List.d.ts.map +1 -1
  68. package/src/core/collection/list/List.js +0 -1
  69. package/src/core/collection/map/HashMap.d.ts +12 -7
  70. package/src/core/collection/map/HashMap.d.ts.map +1 -1
  71. package/src/core/collection/map/HashMap.js +5 -5
  72. package/src/core/collection/map/ObservedMap.d.ts +1 -2
  73. package/src/core/collection/map/ObservedMap.d.ts.map +1 -1
  74. package/src/core/collection/map/ObservedMap.js +0 -1
  75. package/src/core/collection/table/RowFirstTable.d.ts +1 -0
  76. package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
  77. package/src/core/collection/table/RowFirstTable.js +4 -0
  78. package/src/core/color/Color.d.ts +1 -2
  79. package/src/core/color/Color.d.ts.map +1 -1
  80. package/src/core/color/Color.js +5 -4
  81. package/src/core/color/hsv/hsv2rgb_float.d.ts +23 -1
  82. package/src/core/color/hsv/hsv2rgb_float.d.ts.map +1 -1
  83. package/src/core/color/hsv/hsv2rgb_float.js +31 -3
  84. package/src/core/color/hsv/hsv_to_rgb.d.ts +20 -9
  85. package/src/core/color/hsv/hsv_to_rgb.d.ts.map +1 -1
  86. package/src/core/color/hsv/hsv_to_rgb.js +30 -10
  87. package/src/core/color/hsv/hsv_to_rgb_uint8.d.ts.map +1 -1
  88. package/src/core/color/hsv/hsv_to_rgb_uint8.js +10 -5
  89. package/src/core/function/FunctionCompiler.d.ts +9 -4
  90. package/src/core/function/FunctionCompiler.d.ts.map +1 -1
  91. package/src/core/function/FunctionCompiler.js +8 -6
  92. package/src/core/geom/3d/hash-grid/PointHashGrid3.d.ts +118 -0
  93. package/src/core/geom/3d/hash-grid/PointHashGrid3.d.ts.map +1 -0
  94. package/src/core/geom/3d/hash-grid/PointHashGrid3.js +283 -0
  95. package/src/core/geom/3d/quaternion/quat3_shortest_arc.d.ts +41 -0
  96. package/src/core/geom/3d/quaternion/quat3_shortest_arc.d.ts.map +1 -0
  97. package/src/core/geom/3d/quaternion/quat3_shortest_arc.js +94 -0
  98. package/src/core/geom/3d/ray/Ray3.d.ts +5 -5
  99. package/src/core/geom/3d/ray/Ray3.d.ts.map +1 -1
  100. package/src/core/geom/3d/ray/Ray3.js +3 -3
  101. package/src/core/geom/3d/shape/AbstractShape3D.d.ts +1 -0
  102. package/src/core/geom/3d/shape/AbstractShape3D.d.ts.map +1 -1
  103. package/src/core/geom/3d/shape/AbstractShape3D.js +4 -0
  104. package/src/core/geom/3d/shape/BoxShape3D.d.ts.map +1 -1
  105. package/src/core/geom/3d/shape/BoxShape3D.js +6 -17
  106. package/src/core/geom/3d/shape/CapsuleShape3D.d.ts.map +1 -1
  107. package/src/core/geom/3d/shape/CapsuleShape3D.js +3 -14
  108. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts +2 -4
  109. package/src/core/geom/3d/shape/ConvexHullShape3D.d.ts.map +1 -1
  110. package/src/core/geom/3d/shape/ConvexHullShape3D.js +0 -2
  111. package/src/core/geom/3d/shape/CylinderShape3D.d.ts.map +1 -1
  112. package/src/core/geom/3d/shape/CylinderShape3D.js +3 -21
  113. package/src/core/geom/3d/shape/PlaneShape3D.d.ts +105 -0
  114. package/src/core/geom/3d/shape/PlaneShape3D.d.ts.map +1 -0
  115. package/src/core/geom/3d/shape/PlaneShape3D.js +251 -0
  116. package/src/core/geom/3d/shape/SphereShape3D.js +3 -3
  117. package/src/core/geom/3d/shape/TransformedShape3D.d.ts +1 -2
  118. package/src/core/geom/3d/shape/TransformedShape3D.d.ts.map +1 -1
  119. package/src/core/geom/3d/shape/TransformedShape3D.js +0 -1
  120. package/src/core/geom/3d/shape/json/shape_to_type.d.ts.map +1 -1
  121. package/src/core/geom/3d/shape/json/shape_to_type.js +3 -0
  122. package/src/core/geom/3d/shape/json/type_adapters.d.ts +14 -0
  123. package/src/core/geom/3d/shape/json/type_adapters.d.ts.map +1 -1
  124. package/src/core/geom/3d/shape/json/type_adapters.js +15 -0
  125. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts +51 -0
  126. package/src/core/geom/3d/shape/sdf/sdf3_box.d.ts.map +1 -0
  127. package/src/core/geom/3d/shape/sdf/sdf3_box.js +113 -0
  128. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts +46 -0
  129. package/src/core/geom/3d/shape/sdf/sdf3_capsule.d.ts.map +1 -0
  130. package/src/core/geom/3d/shape/sdf/sdf3_capsule.js +82 -0
  131. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts +60 -0
  132. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.d.ts.map +1 -0
  133. package/src/core/geom/3d/shape/sdf/sdf3_cylinder.js +126 -0
  134. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.d.ts +50 -0
  135. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.d.ts.map +1 -0
  136. package/src/core/geom/3d/shape/sdf/sdf3_degenerate_normal.js +53 -0
  137. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts +52 -0
  138. package/src/core/geom/3d/shape/sdf/sdf3_plane.d.ts.map +1 -0
  139. package/src/core/geom/3d/shape/sdf/sdf3_plane.js +61 -0
  140. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts +41 -0
  141. package/src/core/geom/3d/shape/sdf/sdf3_sphere.d.ts.map +1 -0
  142. package/src/core/geom/3d/shape/sdf/sdf3_sphere.js +71 -0
  143. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts +6 -0
  144. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.d.ts.map +1 -1
  145. package/src/core/geom/3d/topology/struct/binary/BinaryDataBlock.js +5 -0
  146. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts +2 -0
  147. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.d.ts.map +1 -1
  148. package/src/core/geom/3d/topology/struct/binary/BinaryTopology.js +5 -0
  149. package/src/core/geom/Quaternion.d.ts +1 -0
  150. package/src/core/geom/Quaternion.d.ts.map +1 -1
  151. package/src/core/geom/Quaternion.js +4 -0
  152. package/src/core/geom/Vector1.d.ts +8 -0
  153. package/src/core/geom/Vector1.d.ts.map +1 -1
  154. package/src/core/geom/Vector1.js +6 -0
  155. package/src/core/geom/Vector3.d.ts +6 -0
  156. package/src/core/geom/Vector3.d.ts.map +1 -1
  157. package/src/core/geom/Vector3.js +5 -0
  158. package/src/core/geom/Vector4.d.ts +10 -2
  159. package/src/core/geom/Vector4.d.ts.map +1 -1
  160. package/src/core/geom/Vector4.js +6 -1
  161. package/src/core/geom/packing/max-rect/MaxRectanglesPacker.d.ts +4 -4
  162. package/src/core/geom/packing/max-rect/MaxRectanglesPacker.d.ts.map +1 -1
  163. package/src/core/geom/packing/max-rect/MaxRectanglesPacker.js +1 -1
  164. package/src/core/graph/convert_graph_to_dot_string.d.ts +7 -3
  165. package/src/core/graph/convert_graph_to_dot_string.d.ts.map +1 -1
  166. package/src/core/graph/convert_graph_to_dot_string.js +3 -2
  167. package/src/core/localization/Localization.d.ts +3 -2
  168. package/src/core/localization/Localization.d.ts.map +1 -1
  169. package/src/core/localization/Localization.js +4 -0
  170. package/src/core/math/noise/create_simplex_noise_2d.js +6 -6
  171. package/src/core/model/ObservedValue.d.ts +8 -6
  172. package/src/core/model/ObservedValue.d.ts.map +1 -1
  173. package/src/core/model/ObservedValue.js +3 -1
  174. package/src/core/model/node-graph/Connection.d.ts +1 -0
  175. package/src/core/model/node-graph/Connection.d.ts.map +1 -1
  176. package/src/core/model/node-graph/Connection.js +4 -0
  177. package/src/core/model/node-graph/NodeGraph.d.ts +8 -5
  178. package/src/core/model/node-graph/NodeGraph.d.ts.map +1 -1
  179. package/src/core/model/node-graph/NodeGraph.js +3 -3
  180. package/src/core/model/node-graph/json/serializeNodeGraphToJSON.d.ts +9 -4
  181. package/src/core/model/node-graph/json/serializeNodeGraphToJSON.d.ts.map +1 -1
  182. package/src/core/model/node-graph/json/serializeNodeGraphToJSON.js +4 -3
  183. package/src/core/model/node-graph/node/NodeDescription.d.ts +12 -4
  184. package/src/core/model/node-graph/node/NodeDescription.d.ts.map +1 -1
  185. package/src/core/model/node-graph/node/NodeDescription.js +10 -3
  186. package/src/core/model/node-graph/node/NodeInstance.d.ts +5 -2
  187. package/src/core/model/node-graph/node/NodeInstance.d.ts.map +1 -1
  188. package/src/core/model/node-graph/node/NodeInstance.js +6 -0
  189. package/src/core/model/node-graph/node/NodeInstancePortReference.d.ts +5 -2
  190. package/src/core/model/node-graph/node/NodeInstancePortReference.d.ts.map +1 -1
  191. package/src/core/model/node-graph/node/NodeInstancePortReference.js +6 -0
  192. package/src/core/model/node-graph/node/NodeRegistry.d.ts +2 -1
  193. package/src/core/model/node-graph/node/NodeRegistry.d.ts.map +1 -1
  194. package/src/core/model/node-graph/node/NodeRegistry.js +4 -0
  195. package/src/core/model/node-graph/node/Port.d.ts +12 -5
  196. package/src/core/model/node-graph/node/Port.d.ts.map +1 -1
  197. package/src/core/model/node-graph/node/Port.js +9 -4
  198. package/src/core/model/node-graph/util/convert_node_graph_to_dot_string.d.ts +5 -2
  199. package/src/core/model/node-graph/util/convert_node_graph_to_dot_string.d.ts.map +1 -1
  200. package/src/core/model/node-graph/util/convert_node_graph_to_dot_string.js +2 -1
  201. package/src/core/model/node-graph/util/graph_clone_by_node_subset.d.ts +5 -2
  202. package/src/core/model/node-graph/util/graph_clone_by_node_subset.d.ts.map +1 -1
  203. package/src/core/model/node-graph/util/graph_clone_by_node_subset.js +2 -1
  204. package/src/core/model/node-graph/util/graph_collect_connections_amongst_nodes.d.ts +5 -2
  205. package/src/core/model/node-graph/util/graph_collect_connections_amongst_nodes.d.ts.map +1 -1
  206. package/src/core/model/node-graph/util/graph_collect_connections_amongst_nodes.js +2 -1
  207. package/src/core/primitives/Class.d.ts +15 -0
  208. package/src/core/primitives/Class.d.ts.map +1 -0
  209. package/src/core/primitives/Class.js +18 -0
  210. package/src/core/primitives/TypedArray.d.ts +15 -0
  211. package/src/core/primitives/TypedArray.d.ts.map +1 -0
  212. package/src/core/primitives/TypedArray.js +27 -0
  213. package/src/core/process/ConcurrencyGate.d.ts +103 -0
  214. package/src/core/process/ConcurrencyGate.d.ts.map +1 -0
  215. package/src/core/process/ConcurrencyGate.js +207 -0
  216. package/src/core/process/buildPromiseChain.d.ts +9 -4
  217. package/src/core/process/buildPromiseChain.d.ts.map +1 -1
  218. package/src/core/process/buildPromiseChain.js +4 -3
  219. package/src/core/process/executor/ConcurrentExecutor.d.ts +2 -0
  220. package/src/core/process/executor/ConcurrentExecutor.d.ts.map +1 -1
  221. package/src/core/process/executor/ConcurrentExecutor.js +5 -0
  222. package/src/core/process/task/Task.d.ts +17 -7
  223. package/src/core/process/task/Task.d.ts.map +1 -1
  224. package/src/core/process/task/Task.js +12 -6
  225. package/src/engine/Engine.d.ts +17 -7
  226. package/src/engine/Engine.d.ts.map +1 -1
  227. package/src/engine/Engine.js +9 -4
  228. package/src/engine/EngineConfiguration.d.ts +33 -9
  229. package/src/engine/EngineConfiguration.d.ts.map +1 -1
  230. package/src/engine/EngineConfiguration.js +22 -4
  231. package/src/engine/EngineHarness.d.ts +22 -10
  232. package/src/engine/EngineHarness.d.ts.map +1 -1
  233. package/src/engine/EngineHarness.js +13 -8
  234. package/src/engine/EntityCreator.d.ts +26 -11
  235. package/src/engine/EntityCreator.d.ts.map +1 -1
  236. package/src/engine/EntityCreator.js +16 -13
  237. package/src/engine/animation/EntityAnimation.d.ts +1 -1
  238. package/src/engine/animation/EntityAnimation.d.ts.map +1 -1
  239. package/src/engine/animation/curve/actionProcessorOperations/curveActions.d.ts +28 -18
  240. package/src/engine/animation/curve/actionProcessorOperations/curveActions.d.ts.map +1 -1
  241. package/src/engine/animation/curve/actionProcessorOperations/curveActions.js +9 -8
  242. package/src/engine/animation/curve/draw/build_plot_entity_from_array.d.ts +18 -8
  243. package/src/engine/animation/curve/draw/build_plot_entity_from_array.d.ts.map +1 -1
  244. package/src/engine/animation/curve/draw/build_plot_entity_from_array.js +8 -7
  245. package/src/engine/animation/curve/editor/CurveEditorView.d.ts +22 -11
  246. package/src/engine/animation/curve/editor/CurveEditorView.d.ts.map +1 -1
  247. package/src/engine/animation/curve/editor/CurveEditorView.js +7 -6
  248. package/src/engine/animation/curve/editor/DragHandler.d.ts +12 -7
  249. package/src/engine/animation/curve/editor/DragHandler.d.ts.map +1 -1
  250. package/src/engine/animation/curve/editor/DragHandler.js +4 -3
  251. package/src/engine/animation/curve/editor/KeyframeStateManager.d.ts +3 -2
  252. package/src/engine/animation/curve/editor/KeyframeStateManager.d.ts.map +1 -1
  253. package/src/engine/animation/curve/editor/canvas2dDrawWorldAxisLabels.d.ts +21 -10
  254. package/src/engine/animation/curve/editor/canvas2dDrawWorldAxisLabels.d.ts.map +1 -1
  255. package/src/engine/animation/curve/editor/canvas2dDrawWorldAxisLabels.js +10 -9
  256. package/src/engine/animation/curve/editor/canvas2dPlotCurveLine.d.ts +20 -9
  257. package/src/engine/animation/curve/editor/canvas2dPlotCurveLine.d.ts.map +1 -1
  258. package/src/engine/animation/curve/editor/canvas2dPlotCurveLine.js +9 -8
  259. package/src/engine/animation/curve/editor/canvas2dPlotCurvePoints.d.ts +15 -7
  260. package/src/engine/animation/curve/editor/canvas2dPlotCurvePoints.d.ts.map +1 -1
  261. package/src/engine/animation/curve/editor/canvas2dPlotCurvePoints.js +7 -6
  262. package/src/engine/animation/curve/editor/createKeyframeDraggableAspect.d.ts +39 -18
  263. package/src/engine/animation/curve/editor/createKeyframeDraggableAspect.d.ts.map +1 -1
  264. package/src/engine/animation/curve/editor/createKeyframeDraggableAspect.js +18 -17
  265. package/src/engine/animation/curve/editor/createPanTool.d.ts +13 -5
  266. package/src/engine/animation/curve/editor/createPanTool.d.ts.map +1 -1
  267. package/src/engine/animation/curve/editor/createPanTool.js +5 -4
  268. package/src/engine/animation/curve/editor/createSelectionBoxTool.d.ts +19 -8
  269. package/src/engine/animation/curve/editor/createSelectionBoxTool.d.ts.map +1 -1
  270. package/src/engine/animation/curve/editor/createSelectionBoxTool.js +8 -7
  271. package/src/engine/animation/keyed2/AnimationTrackPlayback.d.ts +6 -2
  272. package/src/engine/animation/keyed2/AnimationTrackPlayback.d.ts.map +1 -1
  273. package/src/engine/animation/keyed2/AnimationTrackPlayback.js +4 -0
  274. package/src/engine/animation/removeEntityGenericEffect.d.ts +13 -6
  275. package/src/engine/animation/removeEntityGenericEffect.d.ts.map +1 -1
  276. package/src/engine/animation/removeEntityGenericEffect.js +6 -5
  277. package/src/engine/animation/removeEntityWithMeshParticlesEffect.d.ts +11 -5
  278. package/src/engine/animation/removeEntityWithMeshParticlesEffect.d.ts.map +1 -1
  279. package/src/engine/animation/removeEntityWithMeshParticlesEffect.js +5 -4
  280. package/src/engine/asset/Asset.d.ts +6 -3
  281. package/src/engine/asset/Asset.d.ts.map +1 -1
  282. package/src/engine/asset/Asset.js +4 -1
  283. package/src/engine/asset/AssetManager.d.ts +52 -21
  284. package/src/engine/asset/AssetManager.d.ts.map +1 -1
  285. package/src/engine/asset/AssetManager.js +30 -12
  286. package/src/engine/asset/AssetRequest.d.ts +4 -2
  287. package/src/engine/asset/AssetRequest.d.ts.map +1 -1
  288. package/src/engine/asset/AssetRequest.js +5 -0
  289. package/src/engine/asset/AssetRequestScope.d.ts +1 -0
  290. package/src/engine/asset/AssetRequestScope.d.ts.map +1 -1
  291. package/src/engine/asset/AssetRequestScope.js +4 -0
  292. package/src/engine/asset/AssetTransformer.d.ts +7 -1
  293. package/src/engine/asset/AssetTransformer.d.ts.map +1 -1
  294. package/src/engine/asset/AssetTransformer.js +5 -0
  295. package/src/engine/asset/PendingAsset.d.ts +8 -0
  296. package/src/engine/asset/PendingAsset.d.ts.map +1 -1
  297. package/src/engine/asset/PendingAsset.js +6 -0
  298. package/src/engine/asset/loaders/ArrayBufferLoader.d.ts +6 -3
  299. package/src/engine/asset/loaders/ArrayBufferLoader.d.ts.map +1 -1
  300. package/src/engine/asset/loaders/ArrayBufferLoader.js +2 -1
  301. package/src/engine/asset/loaders/AssetLoader.d.ts +8 -2
  302. package/src/engine/asset/loaders/AssetLoader.d.ts.map +1 -1
  303. package/src/engine/asset/loaders/AssetLoader.js +5 -0
  304. package/src/engine/asset/loaders/USDSceneBundleAssetLoader.d.ts +10 -0
  305. package/src/engine/asset/loaders/USDSceneBundleAssetLoader.d.ts.map +1 -1
  306. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts +5 -2
  307. package/src/engine/asset/loaders/image/ImageRGBADataLoader.d.ts.map +1 -1
  308. package/src/engine/asset/loaders/image/ImageRGBADataLoader.js +2 -1
  309. package/src/engine/asset/preloader/AssetPreloader.d.ts +2 -1
  310. package/src/engine/asset/preloader/AssetPreloader.d.ts.map +1 -1
  311. package/src/engine/asset/preloader/AssetPreloader.js +4 -0
  312. package/src/engine/development/performance/MetricCollection.d.ts +11 -3
  313. package/src/engine/development/performance/MetricCollection.d.ts.map +1 -1
  314. package/src/engine/development/performance/MetricCollection.js +7 -2
  315. package/src/engine/ecs/Entity.d.ts +3 -2
  316. package/src/engine/ecs/Entity.d.ts.map +1 -1
  317. package/src/engine/ecs/Entity.js +9 -1
  318. package/src/engine/ecs/EntityComponentDataset.d.ts +8 -6
  319. package/src/engine/ecs/EntityComponentDataset.d.ts.map +1 -1
  320. package/src/engine/ecs/EntityComponentDataset.js +9 -0
  321. package/src/engine/ecs/EntityManager.d.ts +2 -1
  322. package/src/engine/ecs/EntityManager.d.ts.map +1 -1
  323. package/src/engine/ecs/EntityManager.js +5 -0
  324. package/src/engine/ecs/EntityObserver.d.ts +4 -0
  325. package/src/engine/ecs/EntityObserver.d.ts.map +1 -1
  326. package/src/engine/ecs/EntityObserver.js +4 -0
  327. package/src/engine/ecs/EntityReference.d.ts +4 -0
  328. package/src/engine/ecs/EntityReference.d.ts.map +1 -1
  329. package/src/engine/ecs/EntityReference.js +4 -0
  330. package/src/engine/ecs/System.d.ts +11 -2
  331. package/src/engine/ecs/System.d.ts.map +1 -1
  332. package/src/engine/ecs/System.js +6 -0
  333. package/src/engine/ecs/async/WorkerSystem.d.ts +10 -0
  334. package/src/engine/ecs/async/WorkerSystem.d.ts.map +1 -1
  335. package/src/engine/ecs/dynamic_actions/actions/definition/SpeakLineActionDescription.d.ts +7 -3
  336. package/src/engine/ecs/dynamic_actions/actions/definition/SpeakLineActionDescription.d.ts.map +1 -1
  337. package/src/engine/ecs/dynamic_actions/actions/definition/SpeakLineActionDescription.js +3 -2
  338. package/src/engine/ecs/dynamic_actions/actions/definition/WriteToBlackboardActionDescription.d.ts +7 -3
  339. package/src/engine/ecs/dynamic_actions/actions/definition/WriteToBlackboardActionDescription.d.ts.map +1 -1
  340. package/src/engine/ecs/dynamic_actions/actions/definition/WriteToBlackboardActionDescription.js +3 -2
  341. package/src/engine/ecs/gui/menu/radial/RadialContextMenu.d.ts +36 -17
  342. package/src/engine/ecs/gui/menu/radial/RadialContextMenu.d.ts.map +1 -1
  343. package/src/engine/ecs/gui/menu/radial/RadialContextMenu.js +17 -16
  344. package/src/engine/ecs/gui/parallax/GuiElementParallaxSystem.d.ts +7 -3
  345. package/src/engine/ecs/gui/parallax/GuiElementParallaxSystem.d.ts.map +1 -1
  346. package/src/engine/ecs/gui/parallax/GuiElementParallaxSystem.js +3 -2
  347. package/src/engine/ecs/ik/InverseKinematics.d.ts +17 -8
  348. package/src/engine/ecs/ik/InverseKinematics.d.ts.map +1 -1
  349. package/src/engine/ecs/ik/InverseKinematics.js +8 -7
  350. package/src/engine/ecs/storage/binary/BinaryClassSerializationAdapter.d.ts +9 -0
  351. package/src/engine/ecs/storage/binary/BinaryClassSerializationAdapter.d.ts.map +1 -1
  352. package/src/engine/ecs/storage/binary/BinaryClassSerializationAdapter.js +9 -0
  353. package/src/engine/ecs/storage/binary/BinaryClassUpgrader.d.ts +4 -0
  354. package/src/engine/ecs/storage/binary/BinaryClassUpgrader.d.ts.map +1 -1
  355. package/src/engine/ecs/storage/binary/BinaryClassUpgrader.js +4 -0
  356. package/src/engine/ecs/storage/binary/BinarySerializationRegistry.d.ts +10 -4
  357. package/src/engine/ecs/storage/binary/BinarySerializationRegistry.d.ts.map +1 -1
  358. package/src/engine/ecs/storage/binary/BinarySerializationRegistry.js +5 -0
  359. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts +5 -2
  360. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.d.ts.map +1 -1
  361. package/src/engine/ecs/storage/binary/collection/BinaryCollectionDeSerializer.js +2 -1
  362. package/src/engine/ecs/storage/binary/collection/BinaryCollectionSerializer.d.ts +7 -3
  363. package/src/engine/ecs/storage/binary/collection/BinaryCollectionSerializer.d.ts.map +1 -1
  364. package/src/engine/ecs/storage/binary/collection/BinaryCollectionSerializer.js +3 -2
  365. package/src/engine/ecs/systems/ScriptSystem.d.ts +1 -0
  366. package/src/engine/ecs/systems/ScriptSystem.d.ts.map +1 -1
  367. package/src/engine/ecs/terrain/ecs/Terrain.d.ts +7 -2
  368. package/src/engine/ecs/terrain/ecs/Terrain.d.ts.map +1 -1
  369. package/src/engine/ecs/terrain/ecs/Terrain.js +8 -0
  370. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSerializationUpgrader_0_1.d.ts +6 -0
  371. package/src/engine/ecs/terrain/ecs/cling/ClingToTerrainSerializationUpgrader_0_1.d.ts.map +1 -1
  372. package/src/engine/ecs/terrain/ecs/layers/TerrainLayer.d.ts +3 -1
  373. package/src/engine/ecs/terrain/ecs/layers/TerrainLayer.d.ts.map +1 -1
  374. package/src/engine/ecs/terrain/ecs/layers/TerrainLayer.js +5 -0
  375. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.d.ts +3 -1
  376. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.d.ts.map +1 -1
  377. package/src/engine/ecs/terrain/ecs/layers/TerrainLayers.js +5 -0
  378. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts +4 -0
  379. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.d.ts.map +1 -1
  380. package/src/engine/ecs/terrain/ecs/splat/SplatMapping.js +4 -0
  381. package/src/engine/ecs/terrain/tiles/TerrainTile.d.ts +7 -2
  382. package/src/engine/ecs/terrain/tiles/TerrainTile.d.ts.map +1 -1
  383. package/src/engine/ecs/terrain/tiles/TerrainTile.js +1 -1
  384. package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts +6 -2
  385. package/src/engine/ecs/terrain/tiles/TerrainTileManager.d.ts.map +1 -1
  386. package/src/engine/ecs/terrain/tiles/TerrainTileManager.js +6 -1
  387. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.d.ts +9 -4
  388. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.d.ts.map +1 -1
  389. package/src/engine/ecs/terrain/util/loadVisibleTerrainTiles.js +4 -3
  390. package/src/engine/ecs/transform/Transform64.d.ts +4 -0
  391. package/src/engine/ecs/transform/Transform64.d.ts.map +1 -1
  392. package/src/engine/ecs/transform/Transform64.js +4 -0
  393. package/src/engine/graphics/CONTEXT_LOSS_RECOVERY_PLAN.md +30 -13
  394. package/src/engine/graphics/camera/CameraShakeBehavior.d.ts +20 -10
  395. package/src/engine/graphics/camera/CameraShakeBehavior.d.ts.map +1 -1
  396. package/src/engine/graphics/camera/CameraShakeBehavior.js +9 -8
  397. package/src/engine/graphics/camera/CameraShakeTraumaBehavior.d.ts +8 -4
  398. package/src/engine/graphics/camera/CameraShakeTraumaBehavior.d.ts.map +1 -1
  399. package/src/engine/graphics/camera/CameraShakeTraumaBehavior.js +3 -2
  400. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts +11 -5
  401. package/src/engine/graphics/camera/makeOrbitalCameraController.d.ts.map +1 -1
  402. package/src/engine/graphics/camera/makeOrbitalCameraController.js +5 -4
  403. package/src/engine/graphics/canvas/canvas2d_draw_grid.d.ts +19 -9
  404. package/src/engine/graphics/canvas/canvas2d_draw_grid.d.ts.map +1 -1
  405. package/src/engine/graphics/canvas/canvas2d_draw_grid.js +9 -8
  406. package/src/engine/graphics/canvas/canvas2d_draw_label.d.ts +17 -8
  407. package/src/engine/graphics/canvas/canvas2d_draw_label.d.ts.map +1 -1
  408. package/src/engine/graphics/canvas/canvas2d_draw_label.js +8 -7
  409. package/src/engine/graphics/canvas/canvas2d_draw_linear_scale.d.ts +21 -10
  410. package/src/engine/graphics/canvas/canvas2d_draw_linear_scale.d.ts.map +1 -1
  411. package/src/engine/graphics/canvas/canvas2d_draw_linear_scale.js +10 -9
  412. package/src/engine/graphics/canvas/canvas2d_plot_data_line.d.ts +18 -8
  413. package/src/engine/graphics/canvas/canvas2d_plot_data_line.d.ts.map +1 -1
  414. package/src/engine/graphics/canvas/canvas2d_plot_data_line.js +8 -7
  415. package/src/engine/graphics/ecs/camera/Camera.d.ts +4 -0
  416. package/src/engine/graphics/ecs/camera/Camera.d.ts.map +1 -1
  417. package/src/engine/graphics/ecs/camera/Camera.js +4 -0
  418. package/src/engine/graphics/ecs/camera/filter/setup_filtered_camera_controller.d.ts +11 -5
  419. package/src/engine/graphics/ecs/camera/filter/setup_filtered_camera_controller.d.ts.map +1 -1
  420. package/src/engine/graphics/ecs/camera/filter/setup_filtered_camera_controller.js +5 -4
  421. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts +1 -1
  422. package/src/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.d.ts.map +1 -1
  423. package/src/engine/graphics/ecs/particles/ParticleEffect.d.ts +5 -5
  424. package/src/engine/graphics/ecs/particles/ParticleEffect.js +5 -5
  425. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.d.ts +19 -9
  426. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.d.ts.map +1 -1
  427. package/src/engine/graphics/ecs/trail3d/make_gradient_stroke.js +9 -8
  428. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts +3 -0
  429. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.d.ts.map +1 -1
  430. package/src/engine/graphics/particles/particular/engine/emitter/ParticleEmitter.js +3 -0
  431. package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.d.ts +9 -4
  432. package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.d.ts.map +1 -1
  433. package/src/engine/graphics/particles/particular/engine/utils/volume/AttributeValue.js +4 -3
  434. package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts +7 -1
  435. package/src/engine/graphics/render/frame_graph/FrameGraph.d.ts.map +1 -1
  436. package/src/engine/graphics/render/frame_graph/FrameGraph.js +5 -0
  437. package/src/engine/graphics/render/frame_graph/FrameGraphNode.d.ts +4 -0
  438. package/src/engine/graphics/render/frame_graph/FrameGraphNode.d.ts.map +1 -1
  439. package/src/engine/graphics/render/frame_graph/FrameGraphNode.js +4 -0
  440. package/src/engine/graphics/render/frame_graph/FramePassBuilder.d.ts +9 -1
  441. package/src/engine/graphics/render/frame_graph/FramePassBuilder.d.ts.map +1 -1
  442. package/src/engine/graphics/render/frame_graph/FramePassBuilder.js +6 -0
  443. package/src/engine/graphics/render/frame_graph/FramePassNode.d.ts +6 -0
  444. package/src/engine/graphics/render/frame_graph/FramePassNode.d.ts.map +1 -1
  445. package/src/engine/graphics/render/frame_graph/FramePassNode.js +5 -0
  446. package/src/engine/graphics/render/frame_graph/FramePassResources.d.ts +7 -1
  447. package/src/engine/graphics/render/frame_graph/FramePassResources.d.ts.map +1 -1
  448. package/src/engine/graphics/render/frame_graph/FramePassResources.js +5 -0
  449. package/src/engine/graphics/render/frame_graph/IFrameGraphContext.d.ts +6 -2
  450. package/src/engine/graphics/render/frame_graph/IFrameGraphContext.d.ts.map +1 -1
  451. package/src/engine/graphics/render/frame_graph/IFrameGraphContext.js +4 -0
  452. package/src/engine/graphics/render/frame_graph/ResourceEntry.d.ts +13 -5
  453. package/src/engine/graphics/render/frame_graph/ResourceEntry.d.ts.map +1 -1
  454. package/src/engine/graphics/render/frame_graph/ResourceEntry.js +7 -1
  455. package/src/engine/graphics/render/frame_graph/ResourceNode.d.ts +9 -3
  456. package/src/engine/graphics/render/frame_graph/ResourceNode.d.ts.map +1 -1
  457. package/src/engine/graphics/render/frame_graph/ResourceNode.js +6 -1
  458. package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.d.ts +5 -1
  459. package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.d.ts.map +1 -1
  460. package/src/engine/graphics/render/frame_graph/resource/RenderResourceManager.js +4 -0
  461. package/src/engine/graphics/render/frame_graph/resource/ResourceDescriptor.d.ts +7 -3
  462. package/src/engine/graphics/render/frame_graph/resource/ResourceDescriptor.d.ts.map +1 -1
  463. package/src/engine/graphics/render/frame_graph/resource/ResourceDescriptor.js +6 -2
  464. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts +5 -2
  465. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.d.ts.map +1 -1
  466. package/src/engine/graphics/texture/atlas/CachingTextureAtlas.js +2 -1
  467. package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.d.ts +9 -4
  468. package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.d.ts.map +1 -1
  469. package/src/engine/graphics/texture/atlas/TextureAtlasDebugger.js +4 -3
  470. package/src/engine/graphics/texture/sampler/Sampler2D.d.ts +4 -0
  471. package/src/engine/graphics/texture/sampler/Sampler2D.d.ts.map +1 -1
  472. package/src/engine/graphics/texture/sampler/Sampler2D.js +4 -0
  473. package/src/engine/graphics/texture/sampler/debug/prototypeSamplerFiltering.js +3 -2
  474. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +0 -5
  475. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  476. package/src/engine/graphics3/GraphicsEngine.d.ts +6 -0
  477. package/src/engine/graphics3/GraphicsEngine.d.ts.map +1 -1
  478. package/src/engine/graphics3/GraphicsEngine.js +9 -0
  479. package/src/engine/graphics3/ParticipatingMedia.d.ts +9 -4
  480. package/src/engine/graphics3/ParticipatingMedia.d.ts.map +1 -1
  481. package/src/engine/graphics3/ParticipatingMedia.js +4 -3
  482. package/src/engine/graphics3/ShadeCameraAdapter.d.ts +5 -1
  483. package/src/engine/graphics3/ShadeCameraAdapter.d.ts.map +1 -1
  484. package/src/engine/graphics3/ShadeCameraAdapter.js +4 -0
  485. package/src/engine/graphics3/TooltipComponentSystem.d.ts +13 -6
  486. package/src/engine/graphics3/TooltipComponentSystem.d.ts.map +1 -1
  487. package/src/engine/graphics3/TooltipComponentSystem.js +6 -5
  488. package/src/engine/graphics3/VolumetricLightMap.d.ts +5 -2
  489. package/src/engine/graphics3/VolumetricLightMap.d.ts.map +1 -1
  490. package/src/engine/graphics3/VolumetricLightMap.js +2 -1
  491. package/src/engine/graphics3/pose/collect_entity_playbacks.d.ts +26 -0
  492. package/src/engine/graphics3/pose/collect_entity_playbacks.d.ts.map +1 -0
  493. package/src/engine/graphics3/pose/collect_entity_playbacks.js +40 -0
  494. package/src/engine/graphics3/pose/query_entity_node_world_pose.d.ts.map +1 -1
  495. package/src/engine/graphics3/pose/query_entity_node_world_pose.js +74 -101
  496. package/src/engine/input/devices/PointerDevice.d.ts.map +1 -1
  497. package/src/engine/input/devices/PointerDevice.js +7 -6
  498. package/src/engine/input/devices/gamepad/GamepadHandle.d.ts +4 -0
  499. package/src/engine/input/devices/gamepad/GamepadHandle.d.ts.map +1 -1
  500. package/src/engine/input/devices/gamepad/GamepadHandle.js +4 -0
  501. package/src/engine/input/devices/touch/observePinch.d.ts +17 -8
  502. package/src/engine/input/devices/touch/observePinch.d.ts.map +1 -1
  503. package/src/engine/input/devices/touch/observePinch.js +8 -7
  504. package/src/engine/input/ecs/components/InputBinding.d.ts +11 -5
  505. package/src/engine/input/ecs/components/InputBinding.d.ts.map +1 -1
  506. package/src/engine/input/ecs/components/InputBinding.js +5 -4
  507. package/src/engine/input/ecs/ism/InputMapSystem.d.ts +4 -0
  508. package/src/engine/input/ecs/ism/InputMapSystem.d.ts.map +1 -1
  509. package/src/engine/input/ecs/systems/InputControllerSystem.d.ts +1 -1
  510. package/src/engine/input/ecs/systems/InputControllerSystem.d.ts.map +1 -1
  511. package/src/engine/input/ecs/util/TerrainCameraTargetSampler.d.ts +5 -2
  512. package/src/engine/input/ecs/util/TerrainCameraTargetSampler.d.ts.map +1 -1
  513. package/src/engine/input/ecs/util/TerrainCameraTargetSampler.js +2 -1
  514. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.d.ts +9 -4
  515. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.d.ts.map +1 -1
  516. package/src/engine/intelligence/behavior/ecs/OrbitingBehavior.js +4 -3
  517. package/src/engine/intelligence/behavior/ecs/SendEventBehavior.d.ts +9 -4
  518. package/src/engine/intelligence/behavior/ecs/SendEventBehavior.d.ts.map +1 -1
  519. package/src/engine/intelligence/behavior/ecs/SendEventBehavior.js +4 -3
  520. package/src/engine/intelligence/behavior/util/syncExecuteBehaviorToCompletion.d.ts +9 -4
  521. package/src/engine/intelligence/behavior/util/syncExecuteBehaviorToCompletion.d.ts.map +1 -1
  522. package/src/engine/intelligence/behavior/util/syncExecuteBehaviorToCompletion.js +4 -3
  523. package/src/engine/intelligence/mcts/MonteCarlo.d.ts +19 -9
  524. package/src/engine/intelligence/mcts/MonteCarlo.d.ts.map +1 -1
  525. package/src/engine/intelligence/mcts/MonteCarlo.js +9 -8
  526. package/src/engine/intelligence/optimization/RandomOptimizer.d.ts +13 -6
  527. package/src/engine/intelligence/optimization/RandomOptimizer.d.ts.map +1 -1
  528. package/src/engine/intelligence/optimization/RandomOptimizer.js +6 -5
  529. package/src/engine/interpolation/InterpolationSystem.d.ts +4 -0
  530. package/src/engine/interpolation/InterpolationSystem.d.ts.map +1 -1
  531. package/src/engine/knowledge/database/StaticKnowledgeDataTable.d.ts +5 -2
  532. package/src/engine/knowledge/database/StaticKnowledgeDataTable.d.ts.map +1 -1
  533. package/src/engine/knowledge/database/StaticKnowledgeDataTable.js +6 -0
  534. package/src/engine/knowledge/database/StaticKnowledgeDataTableDescriptor.d.ts +6 -2
  535. package/src/engine/knowledge/database/StaticKnowledgeDataTableDescriptor.d.ts.map +1 -1
  536. package/src/engine/knowledge/database/StaticKnowledgeDataTableDescriptor.js +4 -0
  537. package/src/engine/knowledge/database/StaticKnowledgeDatabase.d.ts +7 -4
  538. package/src/engine/knowledge/database/StaticKnowledgeDatabase.d.ts.map +1 -1
  539. package/src/engine/knowledge/database/StaticKnowledgeDatabase.js +6 -0
  540. package/src/engine/makeSimpleTaskProgressView.d.ts +10 -4
  541. package/src/engine/makeSimpleTaskProgressView.d.ts.map +1 -1
  542. package/src/engine/makeSimpleTaskProgressView.js +4 -3
  543. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts +20 -9
  544. package/src/engine/navigation/mesh/build/navmesh_build_topology.d.ts.map +1 -1
  545. package/src/engine/navigation/mesh/build/navmesh_build_topology.js +9 -8
  546. package/src/engine/network/sim/ActionLog.d.ts +1 -2
  547. package/src/engine/network/sim/ActionLog.d.ts.map +1 -1
  548. package/src/engine/network/sim/ActionLog.js +0 -1
  549. package/src/engine/network/state/InputRing.d.ts +1 -2
  550. package/src/engine/network/state/InputRing.d.ts.map +1 -1
  551. package/src/engine/network/state/InputRing.js +0 -1
  552. package/src/engine/notify/Notification.d.ts +15 -9
  553. package/src/engine/notify/Notification.d.ts.map +1 -1
  554. package/src/engine/notify/Notification.js +5 -4
  555. package/src/engine/options/OptionAbstract.d.ts +4 -0
  556. package/src/engine/options/OptionAbstract.d.ts.map +1 -1
  557. package/src/engine/options/OptionAbstract.js +4 -0
  558. package/src/engine/options/OptionsView.d.ts +10 -4
  559. package/src/engine/options/OptionsView.d.ts.map +1 -1
  560. package/src/engine/options/OptionsView.js +4 -3
  561. package/src/engine/physics/ccd/linear_sweep.d.ts +4 -7
  562. package/src/engine/physics/ccd/linear_sweep.d.ts.map +1 -1
  563. package/src/engine/physics/ccd/linear_sweep.js +3 -1
  564. package/src/engine/physics/cloth/MEASUREMENTS.md +2239 -0
  565. package/src/engine/physics/cloth/PLAN.md +1967 -0
  566. package/src/engine/physics/cloth/build/ClothProxy.d.ts +147 -0
  567. package/src/engine/physics/cloth/build/ClothProxy.d.ts.map +1 -0
  568. package/src/engine/physics/cloth/build/ClothProxy.js +207 -0
  569. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts +70 -0
  570. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.d.ts.map +1 -0
  571. package/src/engine/physics/cloth/build/cloth_proxy_from_joints.js +256 -0
  572. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts +176 -0
  573. package/src/engine/physics/cloth/collider/ClothColliderIndex.d.ts.map +1 -0
  574. package/src/engine/physics/cloth/collider/ClothColliderIndex.js +435 -0
  575. package/src/engine/physics/cloth/collider/ClothColliderKind.d.ts +12 -0
  576. package/src/engine/physics/cloth/collider/ClothColliderKind.d.ts.map +1 -0
  577. package/src/engine/physics/cloth/collider/ClothColliderKind.js +38 -0
  578. package/src/engine/physics/cloth/collider/ClothColliderRecord.d.ts +120 -0
  579. package/src/engine/physics/cloth/collider/ClothColliderRecord.d.ts.map +1 -0
  580. package/src/engine/physics/cloth/collider/ClothColliderRecord.js +137 -0
  581. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts +25 -0
  582. package/src/engine/physics/cloth/collider/cloth_collider_bounds.d.ts.map +1 -0
  583. package/src/engine/physics/cloth/collider/cloth_collider_bounds.js +190 -0
  584. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts +44 -0
  585. package/src/engine/physics/cloth/collider/cloth_collider_pack.d.ts.map +1 -0
  586. package/src/engine/physics/cloth/collider/cloth_collider_pack.js +173 -0
  587. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts +94 -0
  588. package/src/engine/physics/cloth/collider/cloth_collider_sdf.d.ts.map +1 -0
  589. package/src/engine/physics/cloth/collider/cloth_collider_sdf.js +275 -0
  590. package/src/engine/physics/cloth/ecs/Cloth.d.ts +145 -0
  591. package/src/engine/physics/cloth/ecs/Cloth.d.ts.map +1 -0
  592. package/src/engine/physics/cloth/ecs/Cloth.js +212 -0
  593. package/src/engine/physics/cloth/ecs/ClothCollider.d.ts +126 -0
  594. package/src/engine/physics/cloth/ecs/ClothCollider.d.ts.map +1 -0
  595. package/src/engine/physics/cloth/ecs/ClothCollider.js +199 -0
  596. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts +9 -0
  597. package/src/engine/physics/cloth/ecs/ClothColliderFlags.d.ts.map +1 -0
  598. package/src/engine/physics/cloth/ecs/ClothColliderFlags.js +32 -0
  599. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts +114 -0
  600. package/src/engine/physics/cloth/ecs/ClothColliderSystem.d.ts.map +1 -0
  601. package/src/engine/physics/cloth/ecs/ClothColliderSystem.js +243 -0
  602. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts +208 -0
  603. package/src/engine/physics/cloth/ecs/ClothDynamics.d.ts.map +1 -0
  604. package/src/engine/physics/cloth/ecs/ClothDynamics.js +331 -0
  605. package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.d.ts +10 -0
  606. package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.d.ts.map +1 -0
  607. package/src/engine/physics/cloth/ecs/ClothDynamicsFlags.js +66 -0
  608. package/src/engine/physics/cloth/ecs/ClothExclude.d.ts +45 -0
  609. package/src/engine/physics/cloth/ecs/ClothExclude.d.ts.map +1 -0
  610. package/src/engine/physics/cloth/ecs/ClothExclude.js +59 -0
  611. package/src/engine/physics/cloth/ecs/ClothFlags.d.ts +10 -0
  612. package/src/engine/physics/cloth/ecs/ClothFlags.d.ts.map +1 -0
  613. package/src/engine/physics/cloth/ecs/ClothFlags.js +47 -0
  614. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts +429 -0
  615. package/src/engine/physics/cloth/ecs/ClothInstance.d.ts.map +1 -0
  616. package/src/engine/physics/cloth/ecs/ClothInstance.js +479 -0
  617. package/src/engine/physics/cloth/ecs/ClothRig.d.ts +95 -0
  618. package/src/engine/physics/cloth/ecs/ClothRig.d.ts.map +1 -0
  619. package/src/engine/physics/cloth/ecs/ClothRig.js +140 -0
  620. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts +178 -0
  621. package/src/engine/physics/cloth/ecs/ClothSystem.d.ts.map +1 -0
  622. package/src/engine/physics/cloth/ecs/ClothSystem.js +1021 -0
  623. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts +90 -0
  624. package/src/engine/physics/cloth/ecs/cloth_build_rows.d.ts.map +1 -0
  625. package/src/engine/physics/cloth/ecs/cloth_build_rows.js +374 -0
  626. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.d.ts +186 -0
  627. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.d.ts.map +1 -0
  628. package/src/engine/physics/cloth/ecs/cloth_dynamics_library.js +283 -0
  629. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts +235 -0
  630. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.d.ts.map +1 -0
  631. package/src/engine/physics/cloth/ecs/cloth_dynamics_map.js +271 -0
  632. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts +59 -0
  633. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.d.ts.map +1 -0
  634. package/src/engine/physics/cloth/ecs/cloth_gather_colliders.js +328 -0
  635. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts +13 -0
  636. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.d.ts.map +1 -0
  637. package/src/engine/physics/cloth/ecs/cloth_reconstruct_pose.js +288 -0
  638. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts +58 -0
  639. package/src/engine/physics/cloth/ecs/cloth_seed_rig.d.ts.map +1 -0
  640. package/src/engine/physics/cloth/ecs/cloth_seed_rig.js +199 -0
  641. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts +59 -0
  642. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.d.ts.map +1 -0
  643. package/src/engine/physics/cloth/ecs/cloth_seed_subtree.js +317 -0
  644. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts +70 -0
  645. package/src/engine/physics/cloth/ecs/cloth_write_back.d.ts.map +1 -0
  646. package/src/engine/physics/cloth/ecs/cloth_write_back.js +212 -0
  647. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts +49 -0
  648. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.d.ts.map +1 -0
  649. package/src/engine/physics/cloth/ecs/cloth_write_back_rig.js +115 -0
  650. package/src/engine/physics/cloth/playground/README.md +339 -0
  651. package/src/engine/physics/cloth/playground/cloth_playground_build.d.ts +58 -0
  652. package/src/engine/physics/cloth/playground/cloth_playground_build.d.ts.map +1 -0
  653. package/src/engine/physics/cloth/playground/cloth_playground_build.js +333 -0
  654. package/src/engine/physics/cloth/playground/cloth_playground_draw.d.ts +79 -0
  655. package/src/engine/physics/cloth/playground/cloth_playground_draw.d.ts.map +1 -0
  656. package/src/engine/physics/cloth/playground/cloth_playground_draw.js +519 -0
  657. package/src/engine/physics/cloth/playground/collide.html +178 -0
  658. package/src/engine/physics/cloth/playground/collide_build.d.ts +95 -0
  659. package/src/engine/physics/cloth/playground/collide_build.d.ts.map +1 -0
  660. package/src/engine/physics/cloth/playground/collide_build.js +315 -0
  661. package/src/engine/physics/cloth/playground/collide_main.d.ts +2 -0
  662. package/src/engine/physics/cloth/playground/collide_main.d.ts.map +1 -0
  663. package/src/engine/physics/cloth/playground/collide_main.js +720 -0
  664. package/src/engine/physics/cloth/playground/garment.html +182 -0
  665. package/src/engine/physics/cloth/playground/garment_build.d.ts +78 -0
  666. package/src/engine/physics/cloth/playground/garment_build.d.ts.map +1 -0
  667. package/src/engine/physics/cloth/playground/garment_build.js +359 -0
  668. package/src/engine/physics/cloth/playground/garment_main.d.ts +2 -0
  669. package/src/engine/physics/cloth/playground/garment_main.d.ts.map +1 -0
  670. package/src/engine/physics/cloth/playground/garment_main.js +633 -0
  671. package/src/engine/physics/cloth/playground/index.html +200 -0
  672. package/src/engine/physics/cloth/playground/main.d.ts +2 -0
  673. package/src/engine/physics/cloth/playground/main.d.ts.map +1 -0
  674. package/src/engine/physics/cloth/playground/main.js +876 -0
  675. package/src/engine/physics/cloth/playground/rig.html +187 -0
  676. package/src/engine/physics/cloth/playground/rig_build.d.ts +81 -0
  677. package/src/engine/physics/cloth/playground/rig_build.d.ts.map +1 -0
  678. package/src/engine/physics/cloth/playground/rig_build.js +296 -0
  679. package/src/engine/physics/cloth/playground/rig_main.d.ts +2 -0
  680. package/src/engine/physics/cloth/playground/rig_main.d.ts.map +1 -0
  681. package/src/engine/physics/cloth/playground/rig_main.js +955 -0
  682. package/src/engine/physics/cloth/solver/ClothState.d.ts +526 -0
  683. package/src/engine/physics/cloth/solver/ClothState.d.ts.map +1 -0
  684. package/src/engine/physics/cloth/solver/ClothState.js +967 -0
  685. package/src/engine/physics/cloth/solver/cloth_color_particles.d.ts +67 -0
  686. package/src/engine/physics/cloth/solver/cloth_color_particles.d.ts.map +1 -0
  687. package/src/engine/physics/cloth/solver/cloth_color_particles.js +220 -0
  688. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts +87 -0
  689. package/src/engine/physics/cloth/solver/cloth_contact_find.d.ts.map +1 -0
  690. package/src/engine/physics/cloth/solver/cloth_contact_find.js +343 -0
  691. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts +112 -0
  692. package/src/engine/physics/cloth/solver/cloth_self_find.d.ts.map +1 -0
  693. package/src/engine/physics/cloth/solver/cloth_self_find.js +448 -0
  694. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts +16 -0
  695. package/src/engine/physics/cloth/solver/cloth_solve_vertex.d.ts.map +1 -0
  696. package/src/engine/physics/cloth/solver/cloth_solve_vertex.js +216 -0
  697. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts +204 -0
  698. package/src/engine/physics/cloth/solver/cloth_solver_constants.d.ts.map +1 -0
  699. package/src/engine/physics/cloth/solver/cloth_solver_constants.js +213 -0
  700. package/src/engine/physics/cloth/solver/cloth_step.d.ts +122 -0
  701. package/src/engine/physics/cloth/solver/cloth_step.d.ts.map +1 -0
  702. package/src/engine/physics/cloth/solver/cloth_step.js +401 -0
  703. package/src/engine/physics/cloth/solver/cloth_vertex_accumulator.d.ts +54 -0
  704. package/src/engine/physics/cloth/solver/cloth_vertex_accumulator.d.ts.map +1 -0
  705. package/src/engine/physics/cloth/solver/cloth_vertex_accumulator.js +56 -0
  706. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts +95 -0
  707. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.d.ts.map +1 -0
  708. package/src/engine/physics/cloth/solver/constraint/cloth_backstop_row.js +285 -0
  709. package/src/engine/physics/cloth/solver/constraint/cloth_bend_row.d.ts +125 -0
  710. package/src/engine/physics/cloth/solver/constraint/cloth_bend_row.d.ts.map +1 -0
  711. package/src/engine/physics/cloth/solver/constraint/cloth_bend_row.js +383 -0
  712. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts +129 -0
  713. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.d.ts.map +1 -0
  714. package/src/engine/physics/cloth/solver/constraint/cloth_contact_row.js +501 -0
  715. package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.d.ts +112 -0
  716. package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.d.ts.map +1 -0
  717. package/src/engine/physics/cloth/solver/constraint/cloth_leash_row.js +346 -0
  718. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts +75 -0
  719. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.d.ts.map +1 -0
  720. package/src/engine/physics/cloth/solver/constraint/cloth_self_row.js +202 -0
  721. package/src/engine/physics/cloth/solver/constraint/cloth_stretch_row.d.ts +94 -0
  722. package/src/engine/physics/cloth/solver/constraint/cloth_stretch_row.d.ts.map +1 -0
  723. package/src/engine/physics/cloth/solver/constraint/cloth_stretch_row.js +383 -0
  724. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts +27 -0
  725. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.d.ts.map +1 -0
  726. package/src/engine/physics/cloth/solver/find_non_finite_cloth_state.js +104 -0
  727. package/src/engine/physics/ecs/PhysicsSystem.d.ts +5 -7
  728. package/src/engine/physics/ecs/PhysicsSystem.d.ts.map +1 -1
  729. package/src/engine/physics/ecs/PhysicsSystem.js +4 -1
  730. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.d.ts +6 -0
  731. package/src/engine/physics/ecs/RigidBodySerializationUpgrader_0_1.d.ts.map +1 -1
  732. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  733. package/src/engine/physics/narrowphase/compute_penetration.d.ts +26 -23
  734. package/src/engine/physics/narrowphase/compute_penetration.d.ts.map +1 -1
  735. package/src/engine/physics/narrowphase/compute_penetration.js +25 -4
  736. package/src/engine/physics/narrowphase/narrowphase_step.d.ts +16 -23
  737. package/src/engine/physics/narrowphase/narrowphase_step.d.ts.map +1 -1
  738. package/src/engine/physics/narrowphase/narrowphase_step.js +15 -4
  739. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts +4 -6
  740. package/src/engine/physics/narrowphase/refine_ray_concave.d.ts.map +1 -1
  741. package/src/engine/physics/narrowphase/refine_ray_concave.js +3 -1
  742. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts +6 -6
  743. package/src/engine/physics/narrowphase/refine_ray_hit.d.ts.map +1 -1
  744. package/src/engine/physics/narrowphase/refine_ray_hit.js +5 -1
  745. package/src/engine/physics/queries/shape_cast.d.ts +9 -7
  746. package/src/engine/physics/queries/shape_cast.d.ts.map +1 -1
  747. package/src/engine/physics/queries/shape_cast.js +8 -1
  748. package/src/engine/platform/EnginePlatform.d.ts +4 -0
  749. package/src/engine/platform/EnginePlatform.d.ts.map +1 -1
  750. package/src/engine/platform/EnginePlatform.js +4 -0
  751. package/src/engine/plugin/EnginePlugin.d.ts +9 -0
  752. package/src/engine/plugin/EnginePlugin.d.ts.map +1 -1
  753. package/src/engine/plugin/EnginePlugin.js +9 -0
  754. package/src/engine/plugin/EnginePluginManager.d.ts +13 -2
  755. package/src/engine/plugin/EnginePluginManager.d.ts.map +1 -1
  756. package/src/engine/plugin/EnginePluginManager.js +10 -0
  757. package/src/engine/save/StorageBackedList.d.ts +8 -4
  758. package/src/engine/save/StorageBackedList.d.ts.map +1 -1
  759. package/src/engine/save/StorageBackedList.js +3 -2
  760. package/src/engine/scene/Scene.d.ts +10 -0
  761. package/src/engine/scene/Scene.d.ts.map +1 -1
  762. package/src/engine/scene/Scene.js +7 -0
  763. package/src/engine/scene/SceneManager.d.ts +4 -0
  764. package/src/engine/scene/SceneManager.d.ts.map +1 -1
  765. package/src/engine/scene/SceneManager.js +4 -0
  766. package/src/engine/scene/SerializedScene.d.ts +8 -4
  767. package/src/engine/scene/SerializedScene.d.ts.map +1 -1
  768. package/src/engine/scene/SerializedScene.js +3 -2
  769. package/src/engine/scene/transitionToScene.d.ts +11 -5
  770. package/src/engine/scene/transitionToScene.d.ts.map +1 -1
  771. package/src/engine/scene/transitionToScene.js +5 -4
  772. package/src/engine/simulation/Ticker.d.ts +5 -2
  773. package/src/engine/simulation/Ticker.d.ts.map +1 -1
  774. package/src/engine/simulation/Ticker.js +2 -1
  775. package/src/engine/sound/SoundEngine.d.ts +4 -0
  776. package/src/engine/sound/SoundEngine.d.ts.map +1 -1
  777. package/src/engine/sound/SoundEngine.js +4 -0
  778. package/src/engine/sound/material/concrete/json/deserializeSoundMaterialFromJSON.d.ts +7 -3
  779. package/src/engine/sound/material/concrete/json/deserializeSoundMaterialFromJSON.d.ts.map +1 -1
  780. package/src/engine/sound/material/concrete/json/deserializeSoundMaterialFromJSON.js +3 -2
  781. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts +10 -0
  782. package/src/engine/sound/simulation/ecs/AcousticSimulationSystem.d.ts.map +1 -1
  783. package/src/engine/sound/sopra/SopraEngine.d.ts +5 -0
  784. package/src/engine/sound/sopra/SopraEngine.d.ts.map +1 -1
  785. package/src/engine/sound/sopra/SopraEngine.js +8 -0
  786. package/src/engine/sound/sopra/definition/BusDefinition.d.ts +4 -0
  787. package/src/engine/sound/sopra/definition/BusDefinition.d.ts.map +1 -1
  788. package/src/engine/sound/sopra/definition/BusDefinition.js +4 -0
  789. package/src/engine/sound/sopra/definition/EventDescription.d.ts +1 -0
  790. package/src/engine/sound/sopra/definition/EventDescription.d.ts.map +1 -1
  791. package/src/engine/sound/sopra/definition/EventDescription.js +4 -0
  792. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts +6 -0
  793. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.d.ts.map +1 -1
  794. package/src/engine/sound/sopra/definition/clip/AbstractAudioClip.js +5 -0
  795. package/src/engine/sound/sopra/definition/clip/BlendContainerAudioClip.d.ts +1 -1
  796. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.d.ts +1 -0
  797. package/src/engine/sound/sopra/definition/clip/ContainerAudioClip.d.ts.map +1 -1
  798. package/src/engine/sound/sopra/definition/clip/RandomContainerAudioClip.d.ts +1 -1
  799. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts +10 -2
  800. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.d.ts.map +1 -1
  801. package/src/engine/sound/sopra/definition/clip/SampleAudioClip.js +7 -0
  802. package/src/engine/sound/sopra/definition/clip/SequenceContainerAudioClip.d.ts +1 -1
  803. package/src/engine/sound/sopra/definition/clip/SwitchContainerAudioClip.d.ts +1 -1
  804. package/src/engine/sound/sopra/runtime/BusGraph.d.ts +4 -0
  805. package/src/engine/sound/sopra/runtime/BusGraph.d.ts.map +1 -1
  806. package/src/engine/sound/sopra/runtime/BusGraph.js +4 -0
  807. package/src/engine/sound/sopra/runtime/EventInstance.d.ts +9 -1
  808. package/src/engine/sound/sopra/runtime/EventInstance.d.ts.map +1 -1
  809. package/src/engine/sound/sopra/runtime/EventInstance.js +11 -0
  810. package/src/engine/sound/sopra/runtime/VoiceManager.d.ts +2 -0
  811. package/src/engine/sound/sopra/runtime/VoiceManager.d.ts.map +1 -1
  812. package/src/engine/sound/sopra/runtime/VoiceManager.js +5 -0
  813. package/src/engine/ui/DraggableAspect.d.ts +14 -6
  814. package/src/engine/ui/DraggableAspect.d.ts.map +1 -1
  815. package/src/engine/ui/DraggableAspect.js +6 -5
  816. package/src/engine/ui/GUIEngine.d.ts +73 -26
  817. package/src/engine/ui/GUIEngine.d.ts.map +1 -1
  818. package/src/engine/ui/GUIEngine.js +37 -19
  819. package/src/engine/ui/bindings/DomElementBindingDescription.d.ts +7 -3
  820. package/src/engine/ui/bindings/DomElementBindingDescription.d.ts.map +1 -1
  821. package/src/engine/ui/bindings/DomElementBindingDescription.js +3 -2
  822. package/src/engine/ui/modal/ModalStack.d.ts +4 -0
  823. package/src/engine/ui/modal/ModalStack.d.ts.map +1 -1
  824. package/src/engine/ui/modal/ModalStack.js +4 -0
  825. package/src/engine/ui/notification/AnimatedObjectEmitter.d.ts +4 -0
  826. package/src/engine/ui/notification/AnimatedObjectEmitter.d.ts.map +1 -1
  827. package/src/engine/ui/notification/AnimatedObjectEmitter.js +4 -0
  828. package/src/engine/ui/notification/NotificationManager.d.ts +21 -8
  829. package/src/engine/ui/notification/NotificationManager.d.ts.map +1 -1
  830. package/src/engine/ui/notification/NotificationManager.js +10 -3
  831. package/src/engine/ui/notification/ViewEmitter.d.ts +8 -2
  832. package/src/engine/ui/notification/ViewEmitter.d.ts.map +1 -1
  833. package/src/engine/ui/notification/ViewEmitter.js +6 -1
  834. package/src/engine/ui/scene/SceneGUIContext.d.ts +4 -0
  835. package/src/engine/ui/scene/SceneGUIContext.d.ts.map +1 -1
  836. package/src/engine/ui/scene/SceneGUIContext.js +4 -0
  837. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.d.ts +9 -4
  838. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.d.ts.map +1 -1
  839. package/src/generation/filtering/numeric/util/populateSampler2DFromCellFilter.js +4 -3
  840. package/src/generation/grid/actions/ContinuousGridCellActionWriteObstacle.d.ts +9 -4
  841. package/src/generation/grid/actions/ContinuousGridCellActionWriteObstacle.d.ts.map +1 -1
  842. package/src/generation/grid/actions/ContinuousGridCellActionWriteObstacle.js +4 -3
  843. package/src/generation/grid/generation/GridTaskApplyActionToCells.d.ts +9 -4
  844. package/src/generation/grid/generation/GridTaskApplyActionToCells.d.ts.map +1 -1
  845. package/src/generation/grid/generation/GridTaskApplyActionToCells.js +4 -3
  846. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.d.ts +9 -4
  847. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.d.ts.map +1 -1
  848. package/src/generation/grid/generation/discrete/GridTaskActionRuleSet.js +4 -3
  849. package/src/generation/grid/generation/grid/select/CellSupplierBestN.d.ts +11 -5
  850. package/src/generation/grid/generation/grid/select/CellSupplierBestN.d.ts.map +1 -1
  851. package/src/generation/grid/generation/grid/select/CellSupplierBestN.js +5 -4
  852. package/src/generation/grid/generation/road/GridTaskGenerateRoads.d.ts +9 -4
  853. package/src/generation/grid/generation/road/GridTaskGenerateRoads.d.ts.map +1 -1
  854. package/src/generation/grid/generation/road/GridTaskGenerateRoads.js +12 -10
  855. package/src/generation/grid/generation/util/buildDistanceMapToObjective.d.ts +19 -9
  856. package/src/generation/grid/generation/util/buildDistanceMapToObjective.d.ts.map +1 -1
  857. package/src/generation/grid/generation/util/buildDistanceMapToObjective.js +9 -8
  858. package/src/generation/grid/generation/util/buildPathFromDistanceMap.d.ts +17 -8
  859. package/src/generation/grid/generation/util/buildPathFromDistanceMap.d.ts.map +1 -1
  860. package/src/generation/grid/generation/util/buildPathFromDistanceMap.js +8 -7
  861. package/src/generation/markers/GridActionRuleSet.d.ts +11 -5
  862. package/src/generation/markers/GridActionRuleSet.d.ts.map +1 -1
  863. package/src/generation/markers/GridActionRuleSet.js +5 -4
  864. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.d.ts +9 -4
  865. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.d.ts.map +1 -1
  866. package/src/generation/markers/actions/MarkerNodeActionEntityPlacement.js +4 -3
  867. package/src/generation/markers/actions/MarkerNodeActionImaginary.d.ts +9 -4
  868. package/src/generation/markers/actions/MarkerNodeActionImaginary.d.ts.map +1 -1
  869. package/src/generation/markers/actions/MarkerNodeActionImaginary.js +4 -3
  870. package/src/generation/markers/actions/MarkerProcessingRule.d.ts +11 -5
  871. package/src/generation/markers/actions/MarkerProcessingRule.d.ts.map +1 -1
  872. package/src/generation/markers/actions/MarkerProcessingRule.js +5 -4
  873. package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorClingToTerrain.d.ts +5 -2
  874. package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorClingToTerrain.d.ts.map +1 -1
  875. package/src/generation/markers/actions/placement/MarkerNodeEntityProcessorClingToTerrain.js +2 -1
  876. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.d.ts +13 -6
  877. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.d.ts.map +1 -1
  878. package/src/generation/markers/actions/terrain/MarkerNodeActionPaintTerrain.js +6 -5
  879. package/src/generation/markers/emitter/MarkerNodeEmitterPredicated.d.ts +7 -3
  880. package/src/generation/markers/emitter/MarkerNodeEmitterPredicated.d.ts.map +1 -1
  881. package/src/generation/markers/emitter/MarkerNodeEmitterPredicated.js +3 -2
  882. package/src/generation/placement/GridCellPlacementRule.d.ts +15 -7
  883. package/src/generation/placement/GridCellPlacementRule.d.ts.map +1 -1
  884. package/src/generation/placement/GridCellPlacementRule.js +7 -6
  885. package/src/shade/descriptor/ObjectDescriptorBase.d.ts +8 -3
  886. package/src/shade/descriptor/ObjectDescriptorBase.d.ts.map +1 -1
  887. package/src/shade/descriptor/ObjectDescriptorBase.js +7 -2
  888. package/src/shade/descriptor/binding/BindGroupLayoutDescriptor.d.ts +12 -0
  889. package/src/shade/descriptor/binding/BindGroupLayoutDescriptor.d.ts.map +1 -1
  890. package/src/shade/descriptor/binding/BindGroupLayoutDescriptor.js +8 -0
  891. package/src/shade/descriptor/binding/BufferBindingLayout.d.ts +11 -6
  892. package/src/shade/descriptor/binding/BufferBindingLayout.d.ts.map +1 -1
  893. package/src/shade/descriptor/binding/BufferBindingLayout.js +5 -4
  894. package/src/shade/descriptor/binding/SamplerBindingLayout.d.ts +2 -2
  895. package/src/shade/descriptor/binding/SamplerBindingLayout.d.ts.map +1 -1
  896. package/src/shade/descriptor/binding/SamplerBindingLayout.js +1 -1
  897. package/src/shade/descriptor/binding/StorageTextureBindingLayout.d.ts +2 -2
  898. package/src/shade/descriptor/binding/StorageTextureBindingLayout.d.ts.map +1 -1
  899. package/src/shade/descriptor/binding/StorageTextureBindingLayout.js +1 -1
  900. package/src/shade/descriptor/binding/TextureBindingLayout.d.ts +4 -4
  901. package/src/shade/descriptor/binding/TextureBindingLayout.d.ts.map +1 -1
  902. package/src/shade/descriptor/binding/TextureBindingLayout.js +2 -2
  903. package/src/shade/descriptor/blend/BlendComponent.d.ts +6 -6
  904. package/src/shade/descriptor/blend/BlendComponent.d.ts.map +1 -1
  905. package/src/shade/descriptor/blend/BlendComponent.js +3 -3
  906. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.d.ts +12 -0
  907. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.d.ts.map +1 -1
  908. package/src/shade/descriptor/pipeline/PipelineLayoutDescriptor.js +8 -0
  909. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts +18 -5
  910. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.d.ts.map +1 -1
  911. package/src/shade/descriptor/pipeline/compute/ComputePipelineDescriptor.js +14 -4
  912. package/src/shade/descriptor/pipeline/render/DepthStencilState.d.ts +4 -1
  913. package/src/shade/descriptor/pipeline/render/DepthStencilState.d.ts.map +1 -1
  914. package/src/shade/descriptor/pipeline/render/DepthStencilState.js +3 -0
  915. package/src/shade/descriptor/pipeline/render/FragmentState.d.ts +5 -2
  916. package/src/shade/descriptor/pipeline/render/FragmentState.d.ts.map +1 -1
  917. package/src/shade/descriptor/pipeline/render/FragmentState.js +4 -1
  918. package/src/shade/descriptor/pipeline/render/PrimitiveState.d.ts +8 -8
  919. package/src/shade/descriptor/pipeline/render/PrimitiveState.d.ts.map +1 -1
  920. package/src/shade/descriptor/pipeline/render/PrimitiveState.js +4 -4
  921. package/src/shade/descriptor/pipeline/render/StencilFaceState.d.ts +16 -4
  922. package/src/shade/descriptor/pipeline/render/StencilFaceState.d.ts.map +1 -1
  923. package/src/shade/descriptor/pipeline/render/StencilFaceState.js +12 -0
  924. package/src/shade/descriptor/pipeline/render/VertexState.d.ts +5 -2
  925. package/src/shade/descriptor/pipeline/render/VertexState.d.ts.map +1 -1
  926. package/src/shade/descriptor/pipeline/render/VertexState.js +4 -1
  927. package/src/shade/descriptor/texture/SamplerDescriptor.d.ts +24 -6
  928. package/src/shade/descriptor/texture/SamplerDescriptor.d.ts.map +1 -1
  929. package/src/shade/descriptor/texture/SamplerDescriptor.js +18 -0
  930. package/src/shade/descriptor/texture/TextureDescriptor.d.ts +2 -2
  931. package/src/shade/descriptor/texture/TextureDescriptor.d.ts.map +1 -1
  932. package/src/shade/descriptor/texture/TextureDescriptor.js +1 -1
  933. package/src/shade/device/ShadeGPUCommandContext.d.ts +46 -12
  934. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  935. package/src/shade/device/ShadeGPUCommandContext.js +28 -8
  936. package/src/shade/device/graph/GraphicsExecutionContext.d.ts +6 -0
  937. package/src/shade/device/graph/GraphicsExecutionContext.d.ts.map +1 -1
  938. package/src/shade/device/graph/GraphicsExecutionContext.js +5 -0
  939. package/src/shade/device/graph/TextureResourceDescriptor.d.ts +13 -6
  940. package/src/shade/device/graph/TextureResourceDescriptor.d.ts.map +1 -1
  941. package/src/shade/device/graph/TextureResourceDescriptor.js +6 -5
  942. package/src/shade/device/pass/ShadeGPUComputePassEncoder.d.ts +6 -0
  943. package/src/shade/device/pass/ShadeGPUComputePassEncoder.d.ts.map +1 -1
  944. package/src/shade/device/pass/ShadeGPUComputePassEncoder.js +5 -0
  945. package/src/shade/device/pass/ShadeGPURenderPassEncoder.d.ts +6 -0
  946. package/src/shade/device/pass/ShadeGPURenderPassEncoder.d.ts.map +1 -1
  947. package/src/shade/device/pass/ShadeGPURenderPassEncoder.js +5 -0
  948. package/src/shade/device/pipeline/ComputePipelineManager.d.ts +9 -1
  949. package/src/shade/device/pipeline/ComputePipelineManager.d.ts.map +1 -1
  950. package/src/shade/device/pipeline/ComputePipelineManager.js +6 -0
  951. package/src/shade/device/pipeline/PipelineLayoutManager.d.ts +6 -0
  952. package/src/shade/device/pipeline/PipelineLayoutManager.d.ts.map +1 -1
  953. package/src/shade/device/pipeline/PipelineLayoutManager.js +5 -0
  954. package/src/shade/device/pipeline/RenderPipelineManager.d.ts +7 -1
  955. package/src/shade/device/pipeline/RenderPipelineManager.d.ts.map +1 -1
  956. package/src/shade/device/pipeline/RenderPipelineManager.js +5 -0
  957. package/src/shade/device/shader/ShaderModuleCompiler.d.ts +4 -0
  958. package/src/shade/device/shader/ShaderModuleCompiler.d.ts.map +1 -1
  959. package/src/shade/device/shader/ShaderModuleCompiler.js +4 -0
  960. package/src/shade/device/timing/profile/GPUFrameRecorder.d.ts +6 -0
  961. package/src/shade/device/timing/profile/GPUFrameRecorder.d.ts.map +1 -1
  962. package/src/shade/device/timing/profile/GPUFrameRecorder.js +5 -0
  963. package/src/shade/device/timing/profile/GPUProfileFrame.d.ts +6 -0
  964. package/src/shade/device/timing/profile/GPUProfileFrame.d.ts.map +1 -1
  965. package/src/shade/device/timing/profile/GPUProfileFrame.js +5 -0
  966. package/src/shade/device/timing/profile/GPUProfileSession.d.ts +1 -0
  967. package/src/shade/device/timing/profile/GPUProfileSession.d.ts.map +1 -1
  968. package/src/shade/device/timing/profile/GPUProfileSession.js +4 -0
  969. package/src/shade/device/timing/profile/GPUProfileSpan.d.ts +4 -0
  970. package/src/shade/device/timing/profile/GPUProfileSpan.d.ts.map +1 -1
  971. package/src/shade/device/timing/profile/GPUProfileSpan.js +4 -0
  972. package/src/shade/playground/add_random_meshes.d.ts +19 -8
  973. package/src/shade/playground/add_random_meshes.d.ts.map +1 -1
  974. package/src/shade/playground/add_random_meshes.js +8 -7
  975. package/src/shade/playground/ground_seam/capture_scene_color.d.ts +4 -0
  976. package/src/shade/playground/ground_seam/capture_scene_color.d.ts.map +1 -1
  977. package/src/shade/playground/particle_ecs/index.html +0 -2
  978. package/src/shade/playground/particle_ecs/main.js +8 -29
  979. package/src/shade/playground/particle_system/main.js +8 -21
  980. package/src/shade/playground/particle_system/particle_prototype.js +8 -8
  981. package/src/shade/playground/particle_system/particle_scene.d.ts +0 -61
  982. package/src/shade/playground/particle_system/particle_scene.d.ts.map +1 -1
  983. package/src/shade/playground/particle_system/particle_scene.js +0 -57
  984. package/src/shade/playground/ssr_variance/reference.d.ts +1 -1
  985. package/src/shade/playground/ssr_variance/reference.d.ts.map +1 -1
  986. package/src/shade/playground/vgeo_viewer/README.md +29 -2
  987. package/src/shade/playground/vgeo_viewer/cut_geometry.d.ts.map +1 -1
  988. package/src/shade/playground/vgeo_viewer/cut_geometry.js +3 -40
  989. package/src/shade/playground/vgeo_viewer/main.js +101 -50
  990. package/src/shade/playground/vgeo_viewer/select_cut.d.ts +19 -0
  991. package/src/shade/playground/vgeo_viewer/select_cut.d.ts.map +1 -1
  992. package/src/shade/playground/vgeo_viewer/select_cut.js +53 -9
  993. package/src/shade/renderer/GPUSamplerManager.d.ts +4 -0
  994. package/src/shade/renderer/GPUSamplerManager.d.ts.map +1 -1
  995. package/src/shade/renderer/GPUSamplerManager.js +4 -0
  996. package/src/shade/renderer/GraphicsContext.d.ts +2 -0
  997. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  998. package/src/shade/renderer/GraphicsContext.js +5 -0
  999. package/src/shade/renderer/Renderer.d.ts +14 -4
  1000. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  1001. package/src/shade/renderer/Renderer.js +70 -67
  1002. package/src/shade/renderer/ResourceManager.d.ts +10 -0
  1003. package/src/shade/renderer/ResourceManager.d.ts.map +1 -1
  1004. package/src/shade/renderer/ResourceManager.js +7 -0
  1005. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +18 -11
  1006. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  1007. package/src/shade/renderer/animation/GPUAnimationManager.js +2063 -1958
  1008. package/src/shade/renderer/animation/ShadeAnimationChannel.d.ts +2 -0
  1009. package/src/shade/renderer/animation/ShadeAnimationChannel.d.ts.map +1 -1
  1010. package/src/shade/renderer/animation/ShadeAnimationChannel.js +5 -0
  1011. package/src/shade/renderer/animation/ShadeAnimationClip.d.ts +4 -0
  1012. package/src/shade/renderer/animation/ShadeAnimationClip.d.ts.map +1 -1
  1013. package/src/shade/renderer/animation/ShadeAnimationClip.js +4 -0
  1014. package/src/shade/renderer/animation/Skin.d.ts +6 -0
  1015. package/src/shade/renderer/animation/Skin.d.ts.map +1 -1
  1016. package/src/shade/renderer/animation/Skin.js +5 -0
  1017. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts +48 -0
  1018. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.d.ts.map +1 -1
  1019. package/src/shade/renderer/animation/skinning/GPUMeshSkinningContext.js +201 -14
  1020. package/src/shade/renderer/binding/BindGroupDescriptor.d.ts +2 -1
  1021. package/src/shade/renderer/binding/BindGroupDescriptor.d.ts.map +1 -1
  1022. package/src/shade/renderer/binding/BindGroupDescriptor.js +4 -0
  1023. package/src/shade/renderer/binding/BindGroupManager.d.ts +1 -0
  1024. package/src/shade/renderer/binding/BindGroupManager.d.ts.map +1 -1
  1025. package/src/shade/renderer/binding/BindGroupManager.js +4 -0
  1026. package/src/shade/renderer/buffer/GPUBufferAllocatorNative.d.ts +4 -0
  1027. package/src/shade/renderer/buffer/GPUBufferAllocatorNative.d.ts.map +1 -1
  1028. package/src/shade/renderer/buffer/GPUBufferAllocatorNative.js +4 -0
  1029. package/src/shade/renderer/buffer/GPUTypedBuffer.d.ts +20 -8
  1030. package/src/shade/renderer/buffer/GPUTypedBuffer.d.ts.map +1 -1
  1031. package/src/shade/renderer/buffer/GPUTypedBuffer.js +11 -6
  1032. package/src/shade/renderer/buffer/graph_inspect_gpu_buffer.d.ts +15 -7
  1033. package/src/shade/renderer/buffer/graph_inspect_gpu_buffer.d.ts.map +1 -1
  1034. package/src/shade/renderer/buffer/graph_inspect_gpu_buffer.js +7 -6
  1035. package/src/shade/renderer/buffer/inspect_gpu_buffer.d.ts +26 -12
  1036. package/src/shade/renderer/buffer/inspect_gpu_buffer.d.ts.map +1 -1
  1037. package/src/shade/renderer/buffer/inspect_gpu_buffer.js +12 -10
  1038. package/src/shade/renderer/buffer/inspect_gpu_buffer_typed.d.ts +13 -6
  1039. package/src/shade/renderer/buffer/inspect_gpu_buffer_typed.d.ts.map +1 -1
  1040. package/src/shade/renderer/buffer/inspect_gpu_buffer_typed.js +6 -5
  1041. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts +13 -5
  1042. package/src/shade/renderer/buffer/table/GPUDatabase.d.ts.map +1 -1
  1043. package/src/shade/renderer/buffer/table/GPUDatabase.js +201 -66
  1044. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts +97 -3
  1045. package/src/shade/renderer/buffer/table/GPUTypedTable.d.ts.map +1 -1
  1046. package/src/shade/renderer/buffer/table/GPUTypedTable.js +419 -2
  1047. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts +4 -0
  1048. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.d.ts.map +1 -1
  1049. package/src/shade/renderer/buffer/table/GPUTypedTableDescriptor.js +4 -0
  1050. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.d.ts +14 -0
  1051. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.d.ts.map +1 -0
  1052. package/src/shade/renderer/buffer/table/GPU_TABLE_PARTIAL_UPLOAD_DESCRIPTOR_WORDS.js +13 -0
  1053. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.d.ts +17 -0
  1054. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.d.ts.map +1 -0
  1055. package/src/shade/renderer/buffer/table/gpu_table_partial_upload_shader.js +116 -0
  1056. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts +11 -4
  1057. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.d.ts.map +1 -1
  1058. package/src/shade/renderer/buffer/table/single/GPUSingleTypeTable.js +9 -3
  1059. package/src/shade/renderer/camera/CameraManager.d.ts +4 -0
  1060. package/src/shade/renderer/camera/CameraManager.d.ts.map +1 -1
  1061. package/src/shade/renderer/camera/CameraManager.js +4 -0
  1062. package/src/shade/renderer/camera/GPUCameraContext.d.ts +4 -2
  1063. package/src/shade/renderer/camera/GPUCameraContext.d.ts.map +1 -1
  1064. package/src/shade/renderer/camera/GPUCameraContext.js +5 -0
  1065. package/src/shade/renderer/extension/FrameContext.d.ts +15 -5
  1066. package/src/shade/renderer/extension/FrameContext.d.ts.map +1 -1
  1067. package/src/shade/renderer/extension/FrameContext.js +7 -0
  1068. package/src/shade/renderer/extension/FrameRecord.d.ts +7 -1
  1069. package/src/shade/renderer/extension/FrameRecord.d.ts.map +1 -1
  1070. package/src/shade/renderer/extension/FrameRecord.js +5 -0
  1071. package/src/shade/renderer/extension/RenderExtension.d.ts +4 -0
  1072. package/src/shade/renderer/extension/RenderExtension.d.ts.map +1 -1
  1073. package/src/shade/renderer/extension/RenderExtension.js +4 -0
  1074. package/src/shade/renderer/geometry/Attribute.d.ts +2 -2
  1075. package/src/shade/renderer/geometry/Attribute.d.ts.map +1 -1
  1076. package/src/shade/renderer/geometry/Attribute.js +1 -1
  1077. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts +30 -2
  1078. package/src/shade/renderer/geometry/GPUGeometryManager.d.ts.map +1 -1
  1079. package/src/shade/renderer/geometry/GPUGeometryManager.js +642 -591
  1080. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts +4 -0
  1081. package/src/shade/renderer/geometry/GPUGeometryMetadata.d.ts.map +1 -1
  1082. package/src/shade/renderer/geometry/GPUGeometryMetadata.js +4 -0
  1083. package/src/shade/renderer/geometry/bvh/GPUBLASRefitBatch.d.ts +7 -3
  1084. package/src/shade/renderer/geometry/bvh/GPUBLASRefitBatch.d.ts.map +1 -1
  1085. package/src/shade/renderer/geometry/bvh/GPUBLASRefitBatch.js +3 -2
  1086. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts +13 -4
  1087. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.d.ts.map +1 -1
  1088. package/src/shade/renderer/geometry/bvh/GPUGeometryBVHManager.js +843 -730
  1089. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts +12 -0
  1090. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.d.ts.map +1 -1
  1091. package/src/shade/renderer/geometry/bvh/GPUGeometryMeshletsBVH.js +13 -0
  1092. package/src/shade/renderer/geometry/bvh/bvh_to_gpu_buffer.d.ts +11 -5
  1093. package/src/shade/renderer/geometry/bvh/bvh_to_gpu_buffer.d.ts.map +1 -1
  1094. package/src/shade/renderer/geometry/bvh/bvh_to_gpu_buffer.js +5 -4
  1095. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts +29 -4
  1096. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.d.ts.map +1 -1
  1097. package/src/shade/renderer/geometry/meshlet/GPUMeshletManager.js +195 -33
  1098. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts +1 -0
  1099. package/src/shade/renderer/geometry/meshlet/MeshletBatch.d.ts.map +1 -1
  1100. package/src/shade/renderer/geometry/meshlet/MeshletBatch.js +4 -0
  1101. package/src/shade/renderer/geometry/meshlet_batch_to_bvh.d.ts +13 -6
  1102. package/src/shade/renderer/geometry/meshlet_batch_to_bvh.d.ts.map +1 -1
  1103. package/src/shade/renderer/geometry/meshlet_batch_to_bvh.js +6 -5
  1104. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts +2 -0
  1105. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.d.ts.map +1 -1
  1106. package/src/shade/renderer/geometry/sdf/GPUGeometrySDFManager.js +5 -0
  1107. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.d.ts +13 -6
  1108. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.d.ts.map +1 -1
  1109. package/src/shade/renderer/geometry/sdf/shader_sdf_visualize.js +6 -5
  1110. package/src/shade/renderer/geometry/virtual/MICRON_COMPARISON_2026_08_20.md +17 -0
  1111. package/src/shade/renderer/geometry/virtual/VGEO_FORMAT.md +141 -24
  1112. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_DESIGN.md +204 -64
  1113. package/src/shade/renderer/geometry/virtual/VIRTUAL_GEOMETRY_PLAN.md +110 -10
  1114. package/src/shade/renderer/geometry/virtual/VK_LOD_CLUSTERS_COMPARISON_2026_08_20.md +19 -0
  1115. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts +19 -6
  1116. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.d.ts.map +1 -1
  1117. package/src/shade/renderer/geometry/virtual/format/read/VGeoByteSource.js +26 -7
  1118. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts +204 -14
  1119. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.d.ts.map +1 -1
  1120. package/src/shade/renderer/geometry/virtual/format/read/VGeoContainerReader.js +803 -51
  1121. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts +49 -13
  1122. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.d.ts.map +1 -1
  1123. package/src/shade/renderer/geometry/virtual/format/read/VGeoReadOptions.js +52 -14
  1124. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.d.ts +20 -0
  1125. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.d.ts.map +1 -0
  1126. package/src/shade/renderer/geometry/virtual/format/read/tool/VGEO_MAX_AUDIT_BYTES.js +19 -0
  1127. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.d.ts +27 -0
  1128. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.d.ts.map +1 -0
  1129. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_audit_container.js +48 -0
  1130. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.d.ts +29 -0
  1131. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.d.ts.map +1 -0
  1132. package/src/shade/renderer/geometry/virtual/format/read/tool/vgeo_install_all_pages.js +60 -0
  1133. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts +8 -1
  1134. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.d.ts.map +1 -1
  1135. package/src/shade/renderer/geometry/virtual/format/read/vgeo_fetch_byte_source.js +32 -2
  1136. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts +258 -0
  1137. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.d.ts.map +1 -0
  1138. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyManager.js +723 -0
  1139. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts +49 -0
  1140. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.d.ts.map +1 -0
  1141. package/src/shade/renderer/geometry/virtual/residency/VGeoResidencyOptions.js +50 -0
  1142. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_bake_for_scene.d.ts +11 -5
  1143. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_bake_for_scene.d.ts.map +1 -1
  1144. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_bake_for_scene.js +5 -4
  1145. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_generate_tree_from_scene.d.ts +12 -5
  1146. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_generate_tree_from_scene.d.ts.map +1 -1
  1147. package/src/shade/renderer/global_illumination/brick4/cpu/brick4_generate_tree_from_scene.js +5 -4
  1148. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts +11 -5
  1149. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.d.ts.map +1 -1
  1150. package/src/shade/renderer/global_illumination/brick4/gpu/bake/brick4_bake_basic.js +5 -4
  1151. package/src/shade/renderer/global_illumination/lpv/GPULightProbeVolume.d.ts +5 -1
  1152. package/src/shade/renderer/global_illumination/lpv/GPULightProbeVolume.d.ts.map +1 -1
  1153. package/src/shade/renderer/global_illumination/lpv/GPULightProbeVolume.js +4 -0
  1154. package/src/shade/renderer/global_illumination/lpv/LightProbeVolume.d.ts +4 -0
  1155. package/src/shade/renderer/global_illumination/lpv/LightProbeVolume.d.ts.map +1 -1
  1156. package/src/shade/renderer/global_illumination/lpv/LightProbeVolume.js +4 -0
  1157. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts +7 -1
  1158. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.d.ts.map +1 -1
  1159. package/src/shade/renderer/global_illumination/lpv/bake/GPULightProbeVolumeRenderer.js +5 -0
  1160. package/src/shade/renderer/global_illumination/lpv/graph_bake_irradiance_lookup_2d.d.ts +11 -5
  1161. package/src/shade/renderer/global_illumination/lpv/graph_bake_irradiance_lookup_2d.d.ts.map +1 -1
  1162. package/src/shade/renderer/global_illumination/lpv/graph_bake_irradiance_lookup_2d.js +5 -4
  1163. package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.d.ts +9 -4
  1164. package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.d.ts.map +1 -1
  1165. package/src/shade/renderer/global_illumination/lpv/lpv_visualise_probes.js +4 -3
  1166. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.d.ts +9 -4
  1167. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.d.ts.map +1 -1
  1168. package/src/shade/renderer/global_illumination/lpv/placement/graph_generate_probe_locations.js +4 -3
  1169. package/src/shade/renderer/global_illumination/lpv/placement/software/lpv_generate_probe_locations2.d.ts +7 -3
  1170. package/src/shade/renderer/global_illumination/lpv/placement/software/lpv_generate_probe_locations2.d.ts.map +1 -1
  1171. package/src/shade/renderer/global_illumination/lpv/placement/software/lpv_generate_probe_locations2.js +3 -2
  1172. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts +28 -8
  1173. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.d.ts.map +1 -1
  1174. package/src/shade/renderer/global_illumination/probe/octahedral/GPUProbeAtlas.js +17 -6
  1175. package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.d.ts +13 -6
  1176. package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.d.ts.map +1 -1
  1177. package/src/shade/renderer/global_illumination/probe/octahedral/border/graph_lpv_paint_border_depth.js +6 -5
  1178. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lpv_store_sh.d.ts +15 -7
  1179. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lpv_store_sh.d.ts.map +1 -1
  1180. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lpv_store_sh.js +7 -6
  1181. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts +17 -8
  1182. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.d.ts.map +1 -1
  1183. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_lvp_atlas_to_sh.js +8 -7
  1184. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.d.ts +17 -8
  1185. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.d.ts.map +1 -1
  1186. package/src/shade/renderer/global_illumination/probe/octahedral/convolve/graph_reduce_atlas_to_sh.js +8 -7
  1187. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.d.ts +15 -7
  1188. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.d.ts.map +1 -1
  1189. package/src/shade/renderer/global_illumination/sharc/SpatialHashRadianceCache.js +7 -6
  1190. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts +19 -9
  1191. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.d.ts.map +1 -1
  1192. package/src/shade/renderer/gpu_primitive/bvh/graph_bvh_build_lbvh.js +9 -8
  1193. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts +9 -4
  1194. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.d.ts.map +1 -1
  1195. package/src/shade/renderer/gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js +4 -3
  1196. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts +11 -5
  1197. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.d.ts.map +1 -1
  1198. package/src/shade/renderer/gpu_primitive/sort/graph_radix_sort.js +5 -4
  1199. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts +9 -1
  1200. package/src/shade/renderer/hiz/HierarchicalZBuffer.d.ts.map +1 -1
  1201. package/src/shade/renderer/hiz/HierarchicalZBuffer.js +6 -0
  1202. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts +11 -5
  1203. package/src/shade/renderer/hiz/generate_downsampler_code.d.ts.map +1 -1
  1204. package/src/shade/renderer/hiz/generate_downsampler_code.js +5 -4
  1205. package/src/shade/renderer/hiz/graph_build_hzb.d.ts +13 -6
  1206. package/src/shade/renderer/hiz/graph_build_hzb.d.ts.map +1 -1
  1207. package/src/shade/renderer/hiz/graph_build_hzb.js +6 -5
  1208. package/src/shade/renderer/light/GPULightCollection.d.ts +4 -1
  1209. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  1210. package/src/shade/renderer/light/GPULightCollection.js +6 -0
  1211. package/src/shade/renderer/light/LightCollection.d.ts +9 -3
  1212. package/src/shade/renderer/light/LightCollection.d.ts.map +1 -1
  1213. package/src/shade/renderer/light/LightCollection.js +5 -0
  1214. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts +11 -5
  1215. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.d.ts.map +1 -1
  1216. package/src/shade/renderer/light/cluster/cull/graph_cull_lights.js +17 -14
  1217. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts +13 -6
  1218. package/src/shade/renderer/light/cluster/graph_assign_clsuters.d.ts.map +1 -1
  1219. package/src/shade/renderer/light/cluster/graph_assign_clsuters.js +6 -5
  1220. package/src/shade/renderer/light/cluster/graph_build_light_clusters.d.ts +13 -6
  1221. package/src/shade/renderer/light/cluster/graph_build_light_clusters.d.ts.map +1 -1
  1222. package/src/shade/renderer/light/cluster/graph_build_light_clusters.js +6 -5
  1223. package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts +7 -3
  1224. package/src/shade/renderer/light/environment/graph_filter_environment_map.d.ts.map +1 -1
  1225. package/src/shade/renderer/light/environment/graph_filter_environment_map.js +3 -2
  1226. package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.d.ts +11 -5
  1227. package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.d.ts.map +1 -1
  1228. package/src/shade/renderer/lightmap/bake/raster/graph_draw_lightmap_viz_buffer.js +5 -4
  1229. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  1230. package/src/shade/renderer/material/GPUMaterialContext.d.ts +17 -1
  1231. package/src/shade/renderer/material/GPUMaterialContext.d.ts.map +1 -1
  1232. package/src/shade/renderer/material/GPUMaterialContext.js +10 -0
  1233. package/src/shade/renderer/material/GPUMaterialManager.d.ts +19 -5
  1234. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  1235. package/src/shade/renderer/material/GPUMaterialManager.js +19 -4
  1236. package/src/shade/renderer/material/ShadeMaterial.d.ts +2 -1
  1237. package/src/shade/renderer/material/ShadeMaterial.d.ts.map +1 -1
  1238. package/src/shade/renderer/material/ShadeMaterial.js +4 -0
  1239. package/src/shade/renderer/material/StandardShadeMaterial.d.ts +6 -0
  1240. package/src/shade/renderer/material/StandardShadeMaterial.d.ts.map +1 -1
  1241. package/src/shade/renderer/material/StandardShadeMaterial.js +5 -0
  1242. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts +12 -0
  1243. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.d.ts.map +1 -1
  1244. package/src/shade/renderer/material/resident/GPUResidentMaterialContext.js +8 -0
  1245. package/src/shade/renderer/particles/DESIGN.md +82 -69
  1246. package/src/shade/renderer/particles/GPUParticleSystem.d.ts +93 -48
  1247. package/src/shade/renderer/particles/GPUParticleSystem.d.ts.map +1 -1
  1248. package/src/shade/renderer/particles/GPUParticleSystem.js +117 -176
  1249. package/src/shade/renderer/particles/ParticleConstants.d.ts +6 -12
  1250. package/src/shade/renderer/particles/ParticleConstants.d.ts.map +1 -1
  1251. package/src/shade/renderer/particles/ParticleConstants.js +6 -13
  1252. package/src/shade/renderer/particles/coherence/graph_particle_bucket.d.ts +4 -4
  1253. package/src/shade/renderer/particles/coherence/graph_particle_bucket.js +6 -6
  1254. package/src/shade/renderer/particles/coherence/particle_program_buckets.d.ts +2 -1
  1255. package/src/shade/renderer/particles/coherence/particle_program_buckets.d.ts.map +1 -1
  1256. package/src/shade/renderer/particles/coherence/particle_program_buckets.js +6 -8
  1257. package/src/shade/renderer/particles/coherence/shader_particle_bucket.d.ts.map +1 -1
  1258. package/src/shade/renderer/particles/coherence/shader_particle_bucket.js +4 -3
  1259. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts +10 -14
  1260. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.d.ts.map +1 -1
  1261. package/src/shade/renderer/particles/data/PARTICLE_COUNTERS.js +10 -14
  1262. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts +6 -5
  1263. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.d.ts.map +1 -1
  1264. package/src/shade/renderer/particles/data/PARTICLE_EMITTER_STRUCT.js +6 -5
  1265. package/src/shade/renderer/particles/graph_particles.d.ts +19 -32
  1266. package/src/shade/renderer/particles/graph_particles.d.ts.map +1 -1
  1267. package/src/shade/renderer/particles/graph_particles.js +18 -25
  1268. package/src/shade/renderer/particles/graph_particles_avboit.js +16 -16
  1269. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts +3 -2
  1270. package/src/shade/renderer/particles/runtime/EmitterRegistry.d.ts.map +1 -1
  1271. package/src/shade/renderer/particles/runtime/EmitterRegistry.js +31 -27
  1272. package/src/shade/renderer/particles/runtime/ParticleEmitter.d.ts +4 -4
  1273. package/src/shade/renderer/particles/runtime/ParticleEmitter.js +4 -4
  1274. package/src/shade/renderer/particles/shaders/chunk_particle_avboit_contribution.js +4 -4
  1275. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts +7 -6
  1276. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.d.ts.map +1 -1
  1277. package/src/shade/renderer/particles/shaders/chunk_particle_billboard_vertex.js +7 -6
  1278. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts +4 -3
  1279. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.d.ts.map +1 -1
  1280. package/src/shade/renderer/particles/shaders/chunk_particle_lighting.js +4 -3
  1281. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.d.ts +2 -2
  1282. package/src/shade/renderer/particles/shaders/chunk_particle_render_math.js +10 -10
  1283. package/src/shade/renderer/particles/shaders/shader_particle_avboit_draw.js +3 -3
  1284. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.d.ts.map +1 -1
  1285. package/src/shade/renderer/particles/shaders/shader_particle_avboit_splat.js +2 -1
  1286. package/src/shade/renderer/particles/shaders/shader_particle_finalize.d.ts.map +1 -1
  1287. package/src/shade/renderer/particles/shaders/shader_particle_finalize.js +13 -16
  1288. package/src/shade/renderer/particles/shaders/shader_particle_render.d.ts.map +1 -1
  1289. package/src/shade/renderer/particles/shaders/shader_particle_render.js +22 -19
  1290. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts +10 -3
  1291. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.d.ts.map +1 -1
  1292. package/src/shade/renderer/path_tracer/accumulating/AccumulatingPathTracer.js +12 -2
  1293. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts +7 -3
  1294. package/src/shade/renderer/path_tracer/graph_path_trace_scene.d.ts.map +1 -1
  1295. package/src/shade/renderer/path_tracer/graph_path_trace_scene.js +3 -2
  1296. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.d.ts +11 -5
  1297. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.d.ts.map +1 -1
  1298. package/src/shade/renderer/postprocess/bloom/graph_bloom_downsample.js +5 -4
  1299. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.d.ts +13 -6
  1300. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.d.ts.map +1 -1
  1301. package/src/shade/renderer/postprocess/bloom/graph_postprocess_bloom.js +11 -9
  1302. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.d.ts +31 -15
  1303. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.d.ts.map +1 -1
  1304. package/src/shade/renderer/postprocess/denoise/LigmaDenoiser.js +15 -14
  1305. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts +21 -10
  1306. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.d.ts.map +1 -1
  1307. package/src/shade/renderer/postprocess/denoise/debug/graph_pass_debug_reprojection.js +10 -9
  1308. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.d.ts +17 -8
  1309. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.d.ts.map +1 -1
  1310. package/src/shade/renderer/postprocess/denoise/graph_atorus_denoise_luma.js +8 -7
  1311. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.d.ts +6 -0
  1312. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.d.ts.map +1 -1
  1313. package/src/shade/renderer/postprocess/dof/gather/DepthOfFieldU.js +5 -0
  1314. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.d.ts +6 -0
  1315. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.d.ts.map +1 -1
  1316. package/src/shade/renderer/postprocess/dof/raymarch/DepthOfField.js +5 -0
  1317. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts +14 -6
  1318. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.d.ts.map +1 -1
  1319. package/src/shade/renderer/postprocess/eye/GPUCameraExposureManager.js +6 -4
  1320. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts +13 -6
  1321. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.d.ts.map +1 -1
  1322. package/src/shade/renderer/postprocess/gtao/graph_build_depth_mipmap.js +6 -5
  1323. package/src/shade/renderer/postprocess/gtao/render_copy_texture.d.ts +28 -13
  1324. package/src/shade/renderer/postprocess/gtao/render_copy_texture.d.ts.map +1 -1
  1325. package/src/shade/renderer/postprocess/gtao/render_copy_texture.js +13 -11
  1326. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.d.ts +4 -0
  1327. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.d.ts.map +1 -1
  1328. package/src/shade/renderer/postprocess/motion_blur/MotionBlur.js +4 -0
  1329. package/src/shade/renderer/postprocess/nss/NSS.d.ts +7 -1
  1330. package/src/shade/renderer/postprocess/nss/NSS.d.ts.map +1 -1
  1331. package/src/shade/renderer/postprocess/nss/NSS.js +5 -0
  1332. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts +9 -4
  1333. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.d.ts.map +1 -1
  1334. package/src/shade/renderer/postprocess/ssr/graph_build_depth_max_pyramid.js +4 -3
  1335. package/src/shade/renderer/postprocess/ssr/ssr_graph_prefilter_color.d.ts +13 -6
  1336. package/src/shade/renderer/postprocess/ssr/ssr_graph_prefilter_color.d.ts.map +1 -1
  1337. package/src/shade/renderer/postprocess/ssr/ssr_graph_prefilter_color.js +6 -5
  1338. package/src/shade/renderer/postprocess/taa/TAA.d.ts +15 -7
  1339. package/src/shade/renderer/postprocess/taa/TAA.d.ts.map +1 -1
  1340. package/src/shade/renderer/postprocess/taa/TAA.js +7 -6
  1341. package/src/shade/renderer/postprocess/velocity/graph_add_camera_velocity.d.ts +13 -6
  1342. package/src/shade/renderer/postprocess/velocity/graph_add_camera_velocity.d.ts.map +1 -1
  1343. package/src/shade/renderer/postprocess/velocity/graph_add_camera_velocity.js +6 -5
  1344. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts +11 -5
  1345. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.d.ts.map +1 -1
  1346. package/src/shade/renderer/rasterize/compute/graph_mesh_frustum_cull.js +5 -4
  1347. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.d.ts +11 -5
  1348. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.d.ts.map +1 -1
  1349. package/src/shade/renderer/rasterize/compute/graph_mesh_sphere_cull.js +5 -4
  1350. package/src/shade/renderer/rasterize/execute_rasterization_jobs.d.ts +12 -5
  1351. package/src/shade/renderer/rasterize/execute_rasterization_jobs.d.ts.map +1 -1
  1352. package/src/shade/renderer/rasterize/execute_rasterization_jobs.js +5 -4
  1353. package/src/shade/renderer/rasterize/expand/GPUCollectionLimits.d.ts +2 -0
  1354. package/src/shade/renderer/rasterize/expand/GPUCollectionLimits.d.ts.map +1 -1
  1355. package/src/shade/renderer/rasterize/expand/GPUCollectionLimits.js +5 -0
  1356. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts +11 -5
  1357. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.d.ts.map +1 -1
  1358. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_meshes_classify_buckets.js +5 -4
  1359. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_scene_meshes_cull_and_classify_buckets.d.ts +9 -4
  1360. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_scene_meshes_cull_and_classify_buckets.d.ts.map +1 -1
  1361. package/src/shade/renderer/rasterize/expand/bucket/mesh/graph_scene_meshes_cull_and_classify_buckets.js +4 -3
  1362. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts +11 -5
  1363. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.d.ts.map +1 -1
  1364. package/src/shade/renderer/rasterize/expand/bucket/meshlet/graph_sort_meshlets_by_bucket_id.js +5 -4
  1365. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts +15 -7
  1366. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.d.ts.map +1 -1
  1367. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_one_way.js +7 -6
  1368. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_two_way.d.ts +15 -7
  1369. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_two_way.d.ts.map +1 -1
  1370. package/src/shade/renderer/rasterize/expand/graph_mesh_filter_two_way.js +7 -6
  1371. package/src/shade/renderer/rasterize/expand/graph_meshlets_expand.d.ts +13 -6
  1372. package/src/shade/renderer/rasterize/expand/graph_meshlets_expand.d.ts.map +1 -1
  1373. package/src/shade/renderer/rasterize/expand/graph_meshlets_expand.js +6 -5
  1374. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts +17 -8
  1375. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.d.ts.map +1 -1
  1376. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_one_way.js +8 -7
  1377. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.d.ts +13 -6
  1378. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.d.ts.map +1 -1
  1379. package/src/shade/renderer/rasterize/expand/graph_meshlets_filter_two_way.js +6 -5
  1380. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts +21 -10
  1381. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.d.ts.map +1 -1
  1382. package/src/shade/renderer/rasterize/expand/graph_meshlets_rasterize.js +10 -9
  1383. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts +13 -6
  1384. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.d.ts.map +1 -1
  1385. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_by_material_side.js +6 -5
  1386. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_one_way.d.ts +17 -8
  1387. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_one_way.d.ts.map +1 -1
  1388. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_one_way.js +8 -7
  1389. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_two_way.d.ts +13 -6
  1390. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_two_way.d.ts.map +1 -1
  1391. package/src/shade/renderer/rasterize/expand/graph_triangles_filter_two_way.js +6 -5
  1392. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts +15 -7
  1393. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.d.ts.map +1 -1
  1394. package/src/shade/renderer/rasterize/expand/mesh/graph_expand_meshes_to_meshlets.js +7 -6
  1395. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts +11 -5
  1396. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.d.ts.map +1 -1
  1397. package/src/shade/renderer/rasterize/fast/graph_extract_meshes_from_bucket.js +5 -4
  1398. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts +9 -4
  1399. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  1400. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +4 -3
  1401. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts +80 -37
  1402. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.d.ts.map +1 -1
  1403. package/src/shade/renderer/rasterize/native/avboit/graph_rasterize_meshes_transparent_avboit.js +37 -33
  1404. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.d.ts +28 -13
  1405. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.d.ts.map +1 -1
  1406. package/src/shade/renderer/rasterize/native/graph_rasterize_meshes_alpha_tested.js +13 -12
  1407. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.d.ts.map +1 -1
  1408. package/src/shade/renderer/rasterize/native/oit/graph_rasterize_meshes_transparent_oit.js +12 -11
  1409. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.d.ts.map +1 -1
  1410. package/src/shade/renderer/rasterize/standard/graph_rasterize_remaining_opaque.js +5 -4
  1411. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts +9 -4
  1412. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  1413. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +4 -3
  1414. package/src/shade/renderer/restir/di/ReSTIRDI.d.ts +10 -0
  1415. package/src/shade/renderer/restir/di/ReSTIRDI.d.ts.map +1 -1
  1416. package/src/shade/renderer/restir/di/ReSTIRDI.js +7 -0
  1417. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts +2 -0
  1418. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.d.ts.map +1 -1
  1419. package/src/shade/renderer/scene/GPUInstancesAccelerationStructure.js +5 -0
  1420. package/src/shade/renderer/scene/GPUSceneContext.d.ts +4 -0
  1421. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  1422. package/src/shade/renderer/scene/GPUSceneContext.js +15 -0
  1423. package/src/shade/renderer/scene/InstanceBatch.d.ts +11 -3
  1424. package/src/shade/renderer/scene/InstanceBatch.d.ts.map +1 -1
  1425. package/src/shade/renderer/scene/InstanceBatch.js +6 -0
  1426. package/src/shade/renderer/scene/Mesh.d.ts +8 -2
  1427. package/src/shade/renderer/scene/Mesh.d.ts.map +1 -1
  1428. package/src/shade/renderer/scene/Mesh.js +6 -1
  1429. package/src/shade/renderer/scene/SceneManager.d.ts +6 -0
  1430. package/src/shade/renderer/scene/SceneManager.d.ts.map +1 -1
  1431. package/src/shade/renderer/scene/SceneManager.js +5 -0
  1432. package/src/shade/renderer/scene/SkinnedMesh.d.ts +4 -0
  1433. package/src/shade/renderer/scene/SkinnedMesh.d.ts.map +1 -1
  1434. package/src/shade/renderer/scene/SkinnedMesh.js +4 -0
  1435. package/src/shade/renderer/scene/serialization/serialize_scene.d.ts +7 -3
  1436. package/src/shade/renderer/scene/serialization/serialize_scene.d.ts.map +1 -1
  1437. package/src/shade/renderer/scene/serialization/serialize_scene.js +3 -2
  1438. package/src/shade/renderer/shader/ComputeShader.d.ts +64 -29
  1439. package/src/shade/renderer/shader/ComputeShader.d.ts.map +1 -1
  1440. package/src/shade/renderer/shader/ComputeShader.js +29 -23
  1441. package/src/shade/renderer/shader/ImageShader.d.ts +9 -4
  1442. package/src/shade/renderer/shader/ImageShader.d.ts.map +1 -1
  1443. package/src/shade/renderer/shader/ImageShader.js +4 -3
  1444. package/src/shade/renderer/shader/ShaderDescriptor.d.ts +10 -4
  1445. package/src/shade/renderer/shader/ShaderDescriptor.d.ts.map +1 -1
  1446. package/src/shade/renderer/shader/ShaderDescriptor.js +4 -3
  1447. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.d.ts +7 -3
  1448. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.d.ts.map +1 -1
  1449. package/src/shade/renderer/shader/chunk/brdf/dfg/generate_dfg_texture.js +6 -4
  1450. package/src/shade/renderer/shader/fullscreen/create_full_quad_pipeline_descriptor.d.ts +13 -6
  1451. package/src/shade/renderer/shader/fullscreen/create_full_quad_pipeline_descriptor.d.ts.map +1 -1
  1452. package/src/shade/renderer/shader/fullscreen/create_full_quad_pipeline_descriptor.js +6 -5
  1453. package/src/shade/renderer/shader/graph/compute/graph_compute_indirect_pass.d.ts +13 -6
  1454. package/src/shade/renderer/shader/graph/compute/graph_compute_indirect_pass.d.ts.map +1 -1
  1455. package/src/shade/renderer/shader/graph/compute/graph_compute_indirect_pass.js +6 -5
  1456. package/src/shade/renderer/shader/graph/compute/graph_compute_pass.d.ts +13 -6
  1457. package/src/shade/renderer/shader/graph/compute/graph_compute_pass.d.ts.map +1 -1
  1458. package/src/shade/renderer/shader/graph/compute/graph_compute_pass.js +6 -5
  1459. package/src/shade/renderer/shader/graph/graph_image_pass.d.ts +27 -13
  1460. package/src/shade/renderer/shader/graph/graph_image_pass.d.ts.map +1 -1
  1461. package/src/shade/renderer/shader/graph/graph_image_pass.js +13 -12
  1462. package/src/shade/renderer/shader/graph/graph_import_raw_buffer.d.ts +15 -7
  1463. package/src/shade/renderer/shader/graph/graph_import_raw_buffer.d.ts.map +1 -1
  1464. package/src/shade/renderer/shader/graph/graph_import_raw_buffer.js +7 -6
  1465. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts +13 -6
  1466. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts.map +1 -1
  1467. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.js +6 -5
  1468. package/src/shade/renderer/shader/pass/RenderPassColorAttachmentDescriptor.d.ts +7 -3
  1469. package/src/shade/renderer/shader/pass/RenderPassColorAttachmentDescriptor.d.ts.map +1 -1
  1470. package/src/shade/renderer/shader/pass/RenderPassColorAttachmentDescriptor.js +3 -2
  1471. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts +49 -23
  1472. package/src/shade/renderer/shader/pass/RenderPassDescriptor.d.ts.map +1 -1
  1473. package/src/shade/renderer/shader/pass/RenderPassDescriptor.js +23 -20
  1474. package/src/shade/renderer/shader/resource/ShaderResourceDescriptor.d.ts +6 -0
  1475. package/src/shade/renderer/shader/resource/ShaderResourceDescriptor.d.ts.map +1 -1
  1476. package/src/shade/renderer/shader/resource/ShaderResourceDescriptor.js +9 -0
  1477. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts +11 -2
  1478. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.d.ts.map +1 -1
  1479. package/src/shade/renderer/shader/resource/ShaderResourceGroupDescriptor.js +7 -1
  1480. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts +10 -3
  1481. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.d.ts.map +1 -1
  1482. package/src/shade/renderer/shader/resource/ShaderResourceSetDescriptor.js +9 -2
  1483. package/src/shade/renderer/shader/type/WGSLAttribute.d.ts +2 -2
  1484. package/src/shade/renderer/shader/type/WGSLAttribute.d.ts.map +1 -1
  1485. package/src/shade/renderer/shader/type/WGSLAttribute.js +1 -1
  1486. package/src/shade/renderer/shader/type/WGSLStructField.d.ts +6 -2
  1487. package/src/shade/renderer/shader/type/WGSLStructField.d.ts.map +1 -1
  1488. package/src/shade/renderer/shader/type/WGSLStructField.js +5 -1
  1489. package/src/shade/renderer/shader/type/WebGPUArray.d.ts +0 -2
  1490. package/src/shade/renderer/shader/type/WebGPUArray.d.ts.map +1 -1
  1491. package/src/shade/renderer/shader/type/WebGPUStruct.d.ts +2 -4
  1492. package/src/shade/renderer/shader/type/WebGPUStruct.d.ts.map +1 -1
  1493. package/src/shade/renderer/shader/type/WebGPUStruct.js +1 -1
  1494. package/src/shade/renderer/shader/type/WebGPUType.d.ts +5 -1
  1495. package/src/shade/renderer/shader/type/WebGPUType.d.ts.map +1 -1
  1496. package/src/shade/renderer/shader/type/WebGPUType.js +4 -0
  1497. package/src/shade/renderer/shadow/evsm/graph_evsm_render_cascade.d.ts +7 -3
  1498. package/src/shade/renderer/shadow/evsm/graph_evsm_render_cascade.d.ts.map +1 -1
  1499. package/src/shade/renderer/shadow/evsm/graph_evsm_render_cascade.js +3 -2
  1500. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts +7 -0
  1501. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.d.ts.map +1 -1
  1502. package/src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js +10 -0
  1503. package/src/shade/renderer/shadow/map/Shadowmap.d.ts +12 -0
  1504. package/src/shade/renderer/shadow/map/Shadowmap.d.ts.map +1 -1
  1505. package/src/shade/renderer/shadow/map/Shadowmap.js +8 -0
  1506. package/src/shade/renderer/shadow/map/SpotLightShadowmap.d.ts +2 -2
  1507. package/src/shade/renderer/shadow/map/csm/graph_csm_setup_cascades.d.ts +7 -3
  1508. package/src/shade/renderer/shadow/map/csm/graph_csm_setup_cascades.d.ts.map +1 -1
  1509. package/src/shade/renderer/shadow/map/csm/graph_csm_setup_cascades.js +3 -2
  1510. package/src/shade/renderer/shadow/map/csm_debug_draw_preview.d.ts +11 -5
  1511. package/src/shade/renderer/shadow/map/csm_debug_draw_preview.d.ts.map +1 -1
  1512. package/src/shade/renderer/shadow/map/csm_debug_draw_preview.js +5 -4
  1513. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.d.ts +13 -6
  1514. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.d.ts.map +1 -1
  1515. package/src/shade/renderer/shadow/ray/staged/gen/graph_gen_rays.js +6 -5
  1516. package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.d.ts +9 -4
  1517. package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.d.ts.map +1 -1
  1518. package/src/shade/renderer/shadow/ray/staged/trace/graph_trace_rays.js +4 -3
  1519. package/src/shade/renderer/shadow/sdf/brixelizer/Brixelizer.d.ts +11 -5
  1520. package/src/shade/renderer/shadow/sdf/brixelizer/Brixelizer.d.ts.map +1 -1
  1521. package/src/shade/renderer/shadow/sdf/brixelizer/Brixelizer.js +5 -4
  1522. package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.d.ts +1 -0
  1523. package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.d.ts.map +1 -1
  1524. package/src/shade/renderer/shadow/sdf/cascade/CascadedSceneSDF.js +4 -0
  1525. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.d.ts +13 -6
  1526. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.d.ts.map +1 -1
  1527. package/src/shade/renderer/shadow/sdf/graph_draw_sdf_shadows.js +6 -5
  1528. package/src/shade/renderer/texture/GPUTextureAllocator.d.ts +4 -0
  1529. package/src/shade/renderer/texture/GPUTextureAllocator.d.ts.map +1 -1
  1530. package/src/shade/renderer/texture/GPUTextureAllocator.js +4 -0
  1531. package/src/shade/renderer/texture/GPUTextureContext.d.ts +1 -0
  1532. package/src/shade/renderer/texture/GPUTextureContext.d.ts.map +1 -1
  1533. package/src/shade/renderer/texture/GPUTextureContext.js +4 -0
  1534. package/src/shade/renderer/texture/RenderTarget.d.ts +7 -3
  1535. package/src/shade/renderer/texture/RenderTarget.d.ts.map +1 -1
  1536. package/src/shade/renderer/texture/RenderTarget.js +3 -2
  1537. package/src/shade/renderer/texture/ShadeTexture.d.ts +2 -1
  1538. package/src/shade/renderer/texture/ShadeTexture.d.ts.map +1 -1
  1539. package/src/shade/renderer/texture/ShadeTexture.js +4 -0
  1540. package/src/shade/renderer/texture/TextureManager.d.ts +1 -0
  1541. package/src/shade/renderer/texture/TextureManager.d.ts.map +1 -1
  1542. package/src/shade/renderer/texture/TextureManager.js +4 -0
  1543. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.d.ts +3 -0
  1544. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.d.ts.map +1 -1
  1545. package/src/shade/renderer/texture/bindless/GPUBindlessTextureManager.js +121 -21
  1546. package/src/shade/renderer/texture/debug_draw_texture.d.ts +9 -4
  1547. package/src/shade/renderer/texture/debug_draw_texture.d.ts.map +1 -1
  1548. package/src/shade/renderer/texture/debug_draw_texture.js +4 -3
  1549. package/src/shade/renderer/texture/download_texture_data.d.ts +17 -6
  1550. package/src/shade/renderer/texture/download_texture_data.d.ts.map +1 -1
  1551. package/src/shade/renderer/texture/download_texture_data.js +10 -5
  1552. package/src/shade/renderer/texture/draw_texture_as_rgba.d.ts +26 -12
  1553. package/src/shade/renderer/texture/draw_texture_as_rgba.d.ts.map +1 -1
  1554. package/src/shade/renderer/texture/draw_texture_as_rgba.js +12 -10
  1555. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.d.ts.map +1 -1
  1556. package/src/shade/renderer/texture/mipmaps/WebGPUMipmapGenerator.js +5 -4
  1557. package/src/shade/renderer/texture/source/ShadeImage.d.ts +4 -2
  1558. package/src/shade/renderer/texture/source/ShadeImage.d.ts.map +1 -1
  1559. package/src/shade/renderer/texture/source/ShadeImage.js +5 -0
  1560. package/src/shade/renderer/texture/source/ShadeImageSerializationAdapter.d.ts +12 -0
  1561. package/src/shade/renderer/texture/source/ShadeImageSerializationAdapter.d.ts.map +1 -1
  1562. package/src/shade/renderer/texture/util/make_checkers_texture.d.ts +12 -5
  1563. package/src/shade/renderer/texture/util/make_checkers_texture.d.ts.map +1 -1
  1564. package/src/shade/renderer/texture/util/make_checkers_texture.js +5 -4
  1565. package/src/shade/renderer/texture/virtual/README.md +6 -4
  1566. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts +7 -2
  1567. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts.map +1 -1
  1568. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +15 -0
  1569. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.d.ts +3 -3
  1570. package/src/shade/renderer/texture/virtual/source/VTSourceTiled.js +3 -3
  1571. package/src/shade/renderer/util/Uint32BufferSamplingStats.d.ts +2 -0
  1572. package/src/shade/renderer/util/Uint32BufferSamplingStats.d.ts.map +1 -1
  1573. package/src/shade/renderer/util/Uint32BufferSamplingStats.js +5 -0
  1574. package/src/shade/renderer/view/GPUViewContext.d.ts +4 -1
  1575. package/src/shade/renderer/view/GPUViewContext.d.ts.map +1 -1
  1576. package/src/shade/renderer/view/GPUViewContext.js +11 -5
  1577. package/src/shade/renderer/view/View.d.ts +6 -0
  1578. package/src/shade/renderer/view/View.d.ts.map +1 -1
  1579. package/src/shade/renderer/view/View.js +5 -0
  1580. package/src/shade/renderer/view/ViewManager.d.ts +11 -1
  1581. package/src/shade/renderer/view/ViewManager.d.ts.map +1 -1
  1582. package/src/shade/renderer/view/ViewManager.js +7 -0
  1583. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts +6 -0
  1584. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.d.ts.map +1 -1
  1585. package/src/shade/renderer/volumetrics/GPUSceneVolumetrics.js +5 -0
  1586. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts +15 -6
  1587. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.d.ts.map +1 -1
  1588. package/src/shade/renderer/volumetrics/GPUViewVolumetrics.js +11 -5
  1589. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts +5 -5
  1590. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.d.ts.map +1 -1
  1591. package/src/shade/renderer/volumetrics/ParticipatingMediaVolume.js +1 -1
  1592. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts +4 -0
  1593. package/src/shade/renderer/volumetrics/SceneVolumetrics.d.ts.map +1 -1
  1594. package/src/shade/renderer/volumetrics/SceneVolumetrics.js +4 -0
  1595. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts +23 -11
  1596. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.d.ts.map +1 -1
  1597. package/src/shade/renderer/volumetrics/build_volumetrics_sun_shadow_transform.js +11 -10
  1598. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts +37 -18
  1599. package/src/shade/renderer/volumetrics/graph_build_volumetrics.d.ts.map +1 -1
  1600. package/src/shade/renderer/volumetrics/graph_build_volumetrics.js +30 -26
  1601. package/src/view/View.d.ts +8 -3
  1602. package/src/view/View.d.ts.map +1 -1
  1603. package/src/view/View.js +7 -2
  1604. package/src/view/asset/AssetLoaderStatusView.d.ts +7 -3
  1605. package/src/view/asset/AssetLoaderStatusView.d.ts.map +1 -1
  1606. package/src/view/asset/AssetLoaderStatusView.js +3 -2
  1607. package/src/view/common/ListView.d.ts +21 -15
  1608. package/src/view/common/ListView.d.ts.map +1 -1
  1609. package/src/view/common/ListView.js +8 -7
  1610. package/src/view/common/LocalizedLabelView.d.ts +15 -7
  1611. package/src/view/common/LocalizedLabelView.d.ts.map +1 -1
  1612. package/src/view/common/LocalizedLabelView.js +7 -6
  1613. package/src/view/common/TabbedView.d.ts +14 -6
  1614. package/src/view/common/TabbedView.d.ts.map +1 -1
  1615. package/src/view/common/TabbedView.js +6 -4
  1616. package/src/view/controller/controls/BooleanVector3Control.d.ts +1 -1
  1617. package/src/view/controller/controls/BooleanVector3Control.d.ts.map +1 -1
  1618. package/src/view/controller/controls/GuiControl.d.ts +1 -1
  1619. package/src/view/controller/controls/GuiControl.d.ts.map +1 -1
  1620. package/src/view/controller/controls/NativeListController.d.ts +15 -7
  1621. package/src/view/controller/controls/NativeListController.d.ts.map +1 -1
  1622. package/src/view/controller/controls/NativeListController.js +7 -6
  1623. package/src/view/controller/controls/Vector1Control.d.ts +1 -1
  1624. package/src/view/controller/controls/Vector1Control.d.ts.map +1 -1
  1625. package/src/view/currency/CompositeCurrencyLabelView.d.ts +10 -5
  1626. package/src/view/currency/CompositeCurrencyLabelView.d.ts.map +1 -1
  1627. package/src/view/currency/CompositeCurrencyLabelView.js +4 -3
  1628. package/src/view/currency/CurrencyDenominationLabelView.d.ts +7 -3
  1629. package/src/view/currency/CurrencyDenominationLabelView.d.ts.map +1 -1
  1630. package/src/view/currency/CurrencyDenominationLabelView.js +3 -2
  1631. package/src/view/elements/CheckboxView.d.ts +7 -3
  1632. package/src/view/elements/CheckboxView.d.ts.map +1 -1
  1633. package/src/view/elements/CheckboxView.js +3 -2
  1634. package/src/view/elements/DropDownSelectionView.d.ts +9 -4
  1635. package/src/view/elements/DropDownSelectionView.d.ts.map +1 -1
  1636. package/src/view/elements/DropDownSelectionView.js +3 -2
  1637. package/src/view/elements/EmptyView.d.ts +29 -13
  1638. package/src/view/elements/EmptyView.d.ts.map +1 -1
  1639. package/src/view/elements/EmptyView.js +13 -11
  1640. package/src/view/elements/SimpleWindow.d.ts +1 -1
  1641. package/src/view/elements/SimpleWindow.d.ts.map +1 -1
  1642. package/src/view/elements/button/ButtonView.d.ts +15 -7
  1643. package/src/view/elements/button/ButtonView.d.ts.map +1 -1
  1644. package/src/view/elements/button/ButtonView.js +7 -6
  1645. package/src/view/elements/image/ImageView.d.ts +7 -3
  1646. package/src/view/elements/image/ImageView.d.ts.map +1 -1
  1647. package/src/view/elements/image/ImageView.js +3 -2
  1648. package/src/view/elements/image/SvgImageView.d.ts +5 -2
  1649. package/src/view/elements/image/SvgImageView.d.ts.map +1 -1
  1650. package/src/view/elements/image/SvgImageView.js +2 -1
  1651. package/src/view/elements/label/LabeledValueView.d.ts +14 -7
  1652. package/src/view/elements/label/LabeledValueView.d.ts.map +1 -1
  1653. package/src/view/elements/label/LabeledValueView.js +6 -5
  1654. package/src/view/elements/progress/SmoothProgressBar.d.ts +14 -7
  1655. package/src/view/elements/progress/SmoothProgressBar.d.ts.map +1 -1
  1656. package/src/view/elements/progress/SmoothProgressBar.js +6 -5
  1657. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts +9 -4
  1658. package/src/view/elements/progress/segmented/SegmentedResourceBarView.d.ts.map +1 -1
  1659. package/src/view/elements/progress/segmented/SegmentedResourceBarView.js +4 -3
  1660. package/src/view/elements/radial/RadialMenu.d.ts +19 -9
  1661. package/src/view/elements/radial/RadialMenu.d.ts.map +1 -1
  1662. package/src/view/elements/radial/RadialMenu.js +9 -8
  1663. package/src/view/elements/radial/RadialMenuElementDefinition.d.ts +63 -31
  1664. package/src/view/elements/radial/RadialMenuElementDefinition.d.ts.map +1 -1
  1665. package/src/view/elements/radial/RadialMenuElementDefinition.js +30 -28
  1666. package/src/view/elements/radial/RadialProgressView.d.ts +1 -1
  1667. package/src/view/elements/radial/RadialProgressView.d.ts.map +1 -1
  1668. package/src/view/elements/radial/RadialText.d.ts +18 -10
  1669. package/src/view/elements/radial/RadialText.d.ts.map +1 -1
  1670. package/src/view/elements/radial/RadialText.js +7 -6
  1671. package/src/view/elements/tiles2d/Tile.d.ts +12 -6
  1672. package/src/view/elements/tiles2d/Tile.d.ts.map +1 -1
  1673. package/src/view/elements/tiles2d/Tile.js +4 -3
  1674. package/src/view/elements/tiles2d/TileGrid.d.ts +23 -10
  1675. package/src/view/elements/tiles2d/TileGrid.d.ts.map +1 -1
  1676. package/src/view/elements/tiles2d/TileGrid.js +10 -9
  1677. package/src/view/elements/video/VideoView.d.ts +7 -3
  1678. package/src/view/elements/video/VideoView.d.ts.map +1 -1
  1679. package/src/view/elements/video/VideoView.js +3 -2
  1680. package/src/view/game/achievements/AchievementNotificationView.d.ts +7 -3
  1681. package/src/view/game/achievements/AchievementNotificationView.d.ts.map +1 -1
  1682. package/src/view/game/achievements/AchievementNotificationView.js +3 -2
  1683. package/src/view/graphics/GraphicsContextFailureView.d.ts +11 -5
  1684. package/src/view/graphics/GraphicsContextFailureView.d.ts.map +1 -1
  1685. package/src/view/graphics/GraphicsContextFailureView.js +5 -4
  1686. package/src/view/interaction/InteractionCommand.d.ts +11 -5
  1687. package/src/view/interaction/InteractionCommand.d.ts.map +1 -1
  1688. package/src/view/interaction/InteractionCommand.js +5 -4
  1689. package/src/view/interaction/InterfaceCommand.d.ts +32 -15
  1690. package/src/view/interaction/InterfaceCommand.d.ts.map +1 -1
  1691. package/src/view/interaction/InterfaceCommand.js +15 -13
  1692. package/src/view/interaction/createInterfaceCommandButton.d.ts +9 -4
  1693. package/src/view/interaction/createInterfaceCommandButton.d.ts.map +1 -1
  1694. package/src/view/interaction/createInterfaceCommandButton.js +4 -3
  1695. package/src/view/layout/DockPanel.d.ts +11 -5
  1696. package/src/view/layout/DockPanel.d.ts.map +1 -1
  1697. package/src/view/layout/DockPanel.js +5 -4
  1698. package/src/view/layout/DrawerView.d.ts +7 -3
  1699. package/src/view/layout/DrawerView.d.ts.map +1 -1
  1700. package/src/view/layout/DrawerView.js +3 -2
  1701. package/src/view/layout/FloatingPanelView.d.ts +7 -3
  1702. package/src/view/layout/FloatingPanelView.d.ts.map +1 -1
  1703. package/src/view/layout/FloatingPanelView.js +3 -2
  1704. package/src/view/layout/SplitView.d.ts +11 -5
  1705. package/src/view/layout/SplitView.d.ts.map +1 -1
  1706. package/src/view/layout/SplitView.js +5 -4
  1707. package/src/view/math/FunctionGraphView.d.ts +8 -4
  1708. package/src/view/math/FunctionGraphView.d.ts.map +1 -1
  1709. package/src/view/math/FunctionGraphView.js +3 -2
  1710. package/src/view/minimap/MinimapCanvasView.d.ts +10 -4
  1711. package/src/view/minimap/MinimapCanvasView.d.ts.map +1 -1
  1712. package/src/view/minimap/MinimapCanvasView.js +4 -3
  1713. package/src/view/minimap/dom/MinimapCameraView.d.ts +2 -1
  1714. package/src/view/minimap/dom/MinimapCameraView.d.ts.map +1 -1
  1715. package/src/view/minimap/dom/MinimapMarkerView.d.ts +31 -9
  1716. package/src/view/minimap/dom/MinimapMarkerView.d.ts.map +1 -1
  1717. package/src/view/minimap/dom/MinimapTerrainView.d.ts +9 -4
  1718. package/src/view/minimap/dom/MinimapTerrainView.d.ts.map +1 -1
  1719. package/src/view/minimap/dom/MinimapTerrainView.js +3 -2
  1720. package/src/view/task/TaskProgressView.d.ts +7 -3
  1721. package/src/view/task/TaskProgressView.d.ts.map +1 -1
  1722. package/src/view/task/TaskProgressView.js +3 -2
  1723. package/src/view/tooltip/DomTooltipManager.d.ts +6 -0
  1724. package/src/view/tooltip/DomTooltipManager.d.ts.map +1 -1
  1725. package/src/view/tooltip/DomTooltipManager.js +5 -0
  1726. package/src/view/tooltip/DomTooltipObserver.d.ts +4 -0
  1727. package/src/view/tooltip/DomTooltipObserver.d.ts.map +1 -1
  1728. package/src/view/tooltip/DomTooltipObserver.js +4 -0
  1729. package/src/view/tooltip/TooltipManager.d.ts +6 -0
  1730. package/src/view/tooltip/TooltipManager.d.ts.map +1 -1
  1731. package/src/view/tooltip/TooltipManager.js +5 -0
  1732. package/src/view/tooltip/TooltipView.d.ts +7 -3
  1733. package/src/view/tooltip/TooltipView.d.ts.map +1 -1
  1734. package/src/view/tooltip/TooltipView.js +5 -1
  1735. package/src/view/tooltip/VisualTip.d.ts +6 -0
  1736. package/src/view/tooltip/VisualTip.d.ts.map +1 -1
  1737. package/src/view/tooltip/VisualTip.js +5 -0
  1738. package/src/view/tooltip/gml/GMLEngine.d.ts +14 -2
  1739. package/src/view/tooltip/gml/GMLEngine.d.ts.map +1 -1
  1740. package/src/view/tooltip/gml/GMLEngine.js +8 -0
  1741. package/src/view/tooltip/gml/compiler/GMLReferenceCompiler.d.ts +10 -0
  1742. package/src/view/tooltip/gml/compiler/GMLReferenceCompiler.d.ts.map +1 -1
  1743. package/src/view/tooltip/gml/compiler/GMLReferenceCompiler.js +7 -0
  1744. package/src/view/util/DomSizeObserver.d.ts +9 -2
  1745. package/src/view/util/DomSizeObserver.d.ts.map +1 -1
  1746. package/src/view/util/DomSizeObserver.js +6 -1
  1747. package/src/engine/graphics/texture/virtual/NOTES.md +0 -128
  1748. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.d.ts +0 -6
  1749. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.d.ts.map +0 -1
  1750. package/src/engine/graphics/texture/virtual/VT_DEFAULT_PAGE_RESOLUTION.js +0 -5
  1751. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts +0 -49
  1752. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.d.ts.map +0 -1
  1753. package/src/engine/graphics/texture/virtual/VirtualTextureTileLoader.js +0 -264
  1754. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.d.ts +0 -44
  1755. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.d.ts.map +0 -1
  1756. package/src/engine/graphics/texture/virtual/VirtualTextureUsage.js +0 -228
  1757. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.d.ts +0 -3
  1758. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.d.ts.map +0 -1
  1759. package/src/engine/graphics/texture/virtual/VirtualTextureUsageShader.js +0 -73
  1760. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.d.ts +0 -14
  1761. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.d.ts.map +0 -1
  1762. package/src/engine/graphics/texture/virtual/debug/ResidencyDebugView.js +0 -85
  1763. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.d.ts +0 -12
  1764. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.d.ts.map +0 -1
  1765. package/src/engine/graphics/texture/virtual/debug/UsageDebugView.js +0 -63
  1766. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.d.ts +0 -12
  1767. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.d.ts.map +0 -1
  1768. package/src/engine/graphics/texture/virtual/debug/UsagePyramidDebugView.js +0 -243
  1769. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.d.ts +0 -30
  1770. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.d.ts.map +0 -1
  1771. package/src/engine/graphics/texture/virtual/tile/VirtualTextureTile.js +0 -39
  1772. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.d.ts +0 -9
  1773. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.d.ts.map +0 -1
  1774. package/src/engine/graphics/texture/virtual/tile/compose_finger_print.js +0 -24
  1775. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.d.ts +0 -9
  1776. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.d.ts.map +0 -1
  1777. package/src/engine/graphics/texture/virtual/tile/compose_tile_address.js +0 -26
  1778. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.d.ts +0 -11
  1779. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.d.ts.map +0 -1
  1780. package/src/engine/graphics/texture/virtual/tile/decompose_finger_print.js +0 -12
  1781. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.d.ts +0 -7
  1782. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.d.ts.map +0 -1
  1783. package/src/engine/graphics/texture/virtual/tile/finger_print_to_tile_address.js +0 -16
  1784. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.d.ts +0 -7
  1785. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.d.ts.map +0 -1
  1786. package/src/engine/graphics/texture/virtual/tile/tile_address_to_finger_print.js +0 -35
  1787. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.d.ts +0 -395
  1788. package/src/shade/renderer/geometry/virtual/format/prototypeVGEOFormatViewer.d.ts.map +0 -1
  1789. package/src/shade/renderer/particles/ParticleRenderMode.d.ts +0 -19
  1790. package/src/shade/renderer/particles/ParticleRenderMode.d.ts.map +0 -1
  1791. package/src/shade/renderer/particles/ParticleRenderMode.js +0 -33
  1792. package/src/shade/renderer/particles/graph_particles_billboard.d.ts +0 -37
  1793. package/src/shade/renderer/particles/graph_particles_billboard.d.ts.map +0 -1
  1794. package/src/shade/renderer/particles/graph_particles_billboard.js +0 -93
  1795. package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts +0 -55
  1796. package/src/shade/renderer/particles/sort/graph_particle_sort.d.ts.map +0 -1
  1797. package/src/shade/renderer/particles/sort/graph_particle_sort.js +0 -130
  1798. package/src/shade/renderer/particles/sort/particle_counting_sort.d.ts +0 -26
  1799. package/src/shade/renderer/particles/sort/particle_counting_sort.d.ts.map +0 -1
  1800. package/src/shade/renderer/particles/sort/particle_counting_sort.js +0 -51
  1801. package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts +0 -14
  1802. package/src/shade/renderer/particles/sort/shader_particle_sort.d.ts.map +0 -1
  1803. package/src/shade/renderer/particles/sort/shader_particle_sort.js +0 -223
@@ -1,1958 +1,2063 @@
1
- import { assert } from "../../../core/assert.js";
2
- import { FrameGraph } from "../../../engine/graphics/render/frame_graph/FrameGraph.js";
3
- import { GPUDatabase } from "../buffer/table/GPUDatabase.js";
4
- import {
5
- graph_scene_update_transform_hierarchy
6
- } from "../scene/hierarchy/graph_scene_update_transform_hierarchy.js";
7
- import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
8
- import {
9
- ANIMATION_BOUND_TRACK_GROUP_NULL,
10
- TRACKS_PER_GROUP_NODE,
11
- } from "./ANIMATION_BOUND_TRACK_GROUP_STRUCT.js";
12
- import { ANIMATION_DATABASE_SPEC } from "./ANIMATION_DATABASE_SPEC.js";
13
- import { AnimationClipFlags } from "./AnimationClipFlags.js";
14
- import { KEYFRAMES_PER_BLOCK } from "./ANIMATION_KEYFRAME_BLOCK_STRUCT.js";
15
- import {
16
- ANIMATION_SKIN_JOINT_BLOCK_NULL,
17
- JOINTS_PER_SKIN_BLOCK,
18
- } from "./ANIMATION_SKIN_JOINT_BLOCK_STRUCT.js";
19
- import { POSE_ACCUMULATOR_SLOT_BYTES } from "./POSE_ACCUMULATOR_LAYOUT.js";
20
- import {
21
- clip_scale_channels,
22
- skin_clip_scale_conflicts
23
- } from "./skin_clip_scale_conflicts.js";
24
- import { skin_normalize_mesh_frame } from "./skin_normalize_mesh_frame.js";
25
- import {
26
- shader_animation_apply,
27
- WORKGROUP_SIZE as ANIMATION_APPLY_WORKGROUP_SIZE,
28
- } from "./shader_animation_apply.js";
29
- import {
30
- shader_animation_pose_resolve,
31
- WORKGROUP_SIZE as ANIMATION_POSE_RESOLVE_WORKGROUP_SIZE,
32
- } from "./shader_animation_pose_resolve.js";
33
- import {
34
- shader_animation_tick,
35
- WORKGROUP_SIZE as ANIMATION_TICK_WORKGROUP_SIZE,
36
- } from "./shader_animation_tick.js";
37
- import {
38
- shader_skin_matrix_prep,
39
- WORKGROUP_SIZE as SKIN_MATRIX_PREP_WORKGROUP_SIZE,
40
- } from "./skinning/shader_skin_matrix_prep.js";
41
-
42
- /**
43
- * Tail-slot placeholders for the fixed-size inline arrays in chain
44
- * nodes. The shader's `count` guard means these bytes are never read,
45
- * but the upload buffer doesn't zero unwritten ranges and
46
- * `write_struct_value` skips undefined fields — so explicit zeros are
47
- * the only way to keep tail bytes deterministic. Frozen so a stray
48
- * mutation can't bleed into multiple slots that share the reference.
49
- */
50
- const ZERO_KEYFRAME = Object.freeze({ time: 0, value: 0, inTangent: 0, outTangent: 0 });
51
- const ZERO_BINDING = Object.freeze({ track: 0, instance: 0, property: 0 });
52
- // Object.freeze on a typed array with elements throws under V8
53
- // ("Cannot freeze array buffer views with elements"), so we leave the
54
- // Float32Array unfrozen — the surrounding placeholder object is still
55
- // frozen to keep accidental field-level mutation out of bounds.
56
- const ZERO_INVERSE_BIND = new Float32Array(16);
57
- const ZERO_SKIN_JOINT = Object.freeze({ node: 0, inverse_bind: ZERO_INVERSE_BIND });
58
-
59
- /**
60
- * One skinning matrix is a `mat4x4f` — 16 f32s, 64 bytes. The
61
- * `skin_matrices` scratch buffer holds one entry per (skin, joint) pair
62
- * across every registered skin.
63
- */
64
- const SKIN_MATRIX_BYTES = 64;
65
-
66
- /**
67
- * Initial capacity of the `skin_matrices` scratch buffer, in matrices.
68
- * 256 × 64 = 16 KB — fits a handful of small skeletons before the first
69
- * grow. Picked to be wasteful neither at "no skins" nor "one character"
70
- * scale; growth doubles, so a few realloc cycles cover anything larger.
71
- */
72
- const SKIN_MATRICES_INITIAL_CAPACITY = 256;
73
-
74
- /**
75
- * Initial capacity of the {@link #pose_accumulator_buffer} in rows
76
- * (= transforms-table slots). 256 × 64 = 16 KB; growth doubles to
77
- * fit larger scenes. Resolve always zeroes the slot after draining,
78
- * so a fresh buffer (createBuffer is zero-initialised) and a "no
79
- * contribution this frame" state are observationally identical.
80
- */
81
- const POSE_ACCUMULATOR_INITIAL_CAPACITY = 256;
82
-
83
- /**
84
- * Per-frame `dt` cap pushed into the tick shader. Without it, a tab
85
- * backgrounded for several seconds (or a paused debugger) would
86
- * leak that whole interval into the next tick — either rocketing a
87
- * looping clip through dozens of cycles in one step or, for
88
- * non-looping clips, clamping them past the meaningful range. The
89
- * cap trades a few frames of CSS-smooth animation after a stall
90
- * for a deterministic clip state.
91
- */
92
- const MAX_DT_SECONDS = 1 / 15;
93
-
94
- /**
95
- * Allocate a singly-linked chain of fixed-capacity nodes into `table`,
96
- * one node per `items_per_chunk`-sized window of `items`. Builds
97
- * tail-first so each node's successor id is known at `add()` time —
98
- * the alternative (forward + back-patch) would mean a second `set()`
99
- * per node.
100
- *
101
- * `build_node(start, count, next_id)` constructs the record to add: it
102
- * populates the inline array from `items[start..start+count]`, pads
103
- * tail slots, and assembles any extra payload the table expects
104
- * (including embedding `next_id` as the node's `next` field).
105
- *
106
- * Returns the head id (the last `add()`'s slot id), or `0xFFFFFFFF` if
107
- * `items` is empty.
108
- *
109
- * @template T
110
- * @template R
111
- * @param {{ add: (record: R) => number }} table
112
- * @param {T[]} items
113
- * @param {number} items_per_chunk
114
- * @param {(start: number, count: number, next_id: number) => R} build_node
115
- * @returns {number}
116
- */
117
- function add_linked_chain(table, items, items_per_chunk, build_node) {
118
- const node_count = Math.ceil(items.length / items_per_chunk);
119
- let next_id = 0xFFFFFFFF;
120
-
121
- for (let n = node_count - 1; n >= 0; n--) {
122
- const start = n * items_per_chunk;
123
- const this_count = Math.min(items_per_chunk, items.length - start);
124
- next_id = table.add(build_node(start, this_count, next_id));
125
- }
126
-
127
- return next_id;
128
- }
129
-
130
- /**
131
- * Top-level coordinator for GPU-resident animation.
132
- *
133
- * Owns a single {@link GPUDatabase} keyed by {@link ANIMATION_DATABASE_SPEC}
134
- * with five tables: `animation_curves`, `animation_keyframe_blocks`,
135
- * `animation_tracks`, `animation_clips`, `animation_bound_track_groups`.
136
- * Every record — including individual keyframes (inlined inside block
137
- * nodes) and individual bindings (inlined inside group nodes) — lives in
138
- * the database, so the dispatch shader needs only one storage binding.
139
- *
140
- * Animation *data* (curves, keyframes, tracks) is independent from
141
- * *bound clips* (each carrying a `time` and a head pointer to its
142
- * bound-track group chain). Bindings are no longer their own table:
143
- * each chain node inlines up to {@link TRACKS_PER_GROUP_NODE} bindings
144
- * and back-references its owning clip.
145
- *
146
- * Usage:
147
- * 1. (Once at setup) Either low-level (`add_curve`, `add_track`,
148
- * `add_skin`, `add_clip`) or high-level (`register_skin`,
149
- * `register_clip` against the CPU data classes {@link Skin} /
150
- * {@link ShadeAnimationClip}).
151
- * 2. (Once) {@link start} per clip to begin GPU-driven playback —
152
- * the {@link shader_animation_tick} pass advances `time` each
153
- * frame thereafter. CPU-driven prototypes can instead call
154
- * {@link set_time} per frame and skip the Playing flag.
155
- * 3. (Per frame) {@link update} flushes pending writes for the
156
- * database. The scene context already does this on its own
157
- * update path.
158
- */
159
- export class GPUAnimationManager {
160
-
161
- /** @type {GPUDevice} */
162
- #device;
163
-
164
- /** @type {string} */
165
- #label;
166
-
167
- /** @type {GPUDatabase} */
168
- #database;
169
-
170
- /**
171
- * Ping-pong pair of per-frame skinning-matrix buffers — one holds
172
- * this frame's matrices (target of the prep dispatch, source for
173
- * skinning's current-position write), the other last frame's
174
- * (source for skinning's `position_prev` write that feeds the
175
- * velocity post-pass).
176
- *
177
- * Ping-pong rather than a single buffer because the prev-frame
178
- * matrices need to survive next frame's prep dispatch — and the
179
- * cheapest way to preserve them is to write the new matrices into
180
- * the OTHER buffer instead of overwriting the only one we have.
181
- *
182
- * {@link #skin_matrices_current_index} selects which entry of this
183
- * array is "current" for the frame about to be dispatched;
184
- * {@link dispatch_skin_matrix_prep} flips it before each frame's
185
- * prep so the now-stale slot gets recycled.
186
- *
187
- * Lazily allocated on first {@link add_skin}; both grow together
188
- * by doubling. After a grow both buffers are zero-initialised and
189
- * {@link #first_frame_skin_matrices} is set so the next prep
190
- * dispatch seeds the "previous" buffer with a copy of the freshly
191
- * written "current" — without this, the first velocity readback
192
- * after a grow would interpolate against zero matrices.
193
- *
194
- * @type {Array<GPUBuffer|null>}
195
- */
196
- #skin_matrices_buffers = [null, null];
197
-
198
- /**
199
- * Which entry of {@link #skin_matrices_buffers} is "current" for
200
- * the next prep dispatch. Flipped at the start of every prep call.
201
- * @type {number}
202
- */
203
- #skin_matrices_current_index = 0;
204
-
205
- /**
206
- * When true, the next prep dispatch will copy the freshly-written
207
- * "current" buffer into the "previous" buffer at the end of the
208
- * pass — used on first allocation and after every grow so the
209
- * very first velocity computation has matching prev/current
210
- * matrices (zero velocity).
211
- * @type {boolean}
212
- */
213
- #first_frame_skin_matrices = true;
214
-
215
- /**
216
- * Capacity of each of {@link #skin_matrices_buffers} in matrices
217
- * (not bytes). Both buffers always have the same capacity.
218
- * @type {number}
219
- */
220
- #skin_matrices_capacity = 0;
221
-
222
- /**
223
- * Next free matrix index. Each {@link add_skin} reserves
224
- * `joint_count` consecutive entries starting here, then advances.
225
- * Monotonic — no freeing in the MVP.
226
- * @type {number}
227
- */
228
- #next_skin_matrix_offset = 0;
229
-
230
- /**
231
- * CPU-side `skin_id → skin_matrix_offset` map. Each registered
232
- * skin's base address in the shared `skin_matrices` scratch
233
- * buffer; the per-block absolute offset (the prep shader's hot
234
- * path) is derived as `base + chain_position *
235
- * JOINTS_PER_SKIN_BLOCK` at registration. Storing the base on
236
- * GPU would duplicate it — the block records already carry the
237
- * per-block absolute address, and only the CPU (the skinning-
238
- * binding upload path) needs the skin's base value.
239
- *
240
- * @type {Map<number, number>}
241
- */
242
- #skin_matrix_offsets = new Map();
243
-
244
- /**
245
- * Registered skins by id, kept for one reason: a clip registered *after* a skin has to be held
246
- * against it, and a clip registered before one has to be held against the skin when it arrives.
247
- * The frame a skinned mesh is deformed in is fixed once, at registration, and a clip that scales
248
- * a joint is the one thing that can undo it afterwards — see {@link skin_clip_scale_conflicts}
249
- * and `SKINNING_FRAME_PLAN.md` §4.
250
- *
251
- * @type {Map<number, Skin>}
252
- */
253
- #skins_by_id = new Map();
254
-
255
- /**
256
- * Registered clips that scale *something*, by id. Almost always empty — a character's clips
257
- * rotate and translate — which is what keeps the pairwise check free in the ordinary case.
258
- *
259
- * @type {Map<number, ShadeAnimationClip>}
260
- */
261
- #scaling_clips = new Map();
262
-
263
- /**
264
- * Per-frame pose-accumulator scratch buffer. One slot per
265
- * `transforms_table` row (slot size = `POSE_ACCUMULATOR_SLOT_BYTES`).
266
- * The apply pass atomic-adds weighted TRS contributions into each
267
- * dirty row; the resolve pass drains it into `transforms_table.local_*`
268
- * and zeros the slot for next frame.
269
- *
270
- * Lazily allocated on first {@link ensure_pose_accumulator_capacity};
271
- * grows by doubling. Grow destroys the old buffer — since resolve
272
- * always leaves it zero, no copy is needed on grow.
273
- *
274
- * @type {GPUBuffer|null}
275
- */
276
- #pose_accumulator_buffer = null;
277
-
278
- /**
279
- * Capacity of {@link #pose_accumulator_buffer} in rows (slot
280
- * indices), not bytes.
281
- * @type {number}
282
- */
283
- #pose_accumulator_capacity = 0;
284
-
285
- /**
286
- * Back-reference to the owning scene context. Lets the high-level
287
- * `register_*` methods resolve `Node3D.id` → transforms-table row
288
- * via {@link GPUSceneContext#id_mapping} and bind skinning meshes
289
- * via {@link GPUSceneContext#skinning}, without callers having to
290
- * thread the context through every call.
291
- *
292
- * The manager doesn't touch this at construction (the scene
293
- * context is itself only partially constructed when it builds the
294
- * manager) — only inside `register_*` calls, which happen after
295
- * scene.build has populated `id_mapping`.
296
- *
297
- * @type {GPUSceneContext}
298
- */
299
- #scene_context;
300
-
301
- /**
302
- * Identity-keyed cache of curve-id-on-GPU for every
303
- * {@link AnimationCurve} that has been registered via
304
- * {@link register_clip}. Lets multiple clips whose channels share
305
- * the same curve instance (e.g. one base clip retargeted across
306
- * many character instances) reuse the same GPU rows for the
307
- * curve header + keyframe blocks rather than uploading
308
- * duplicates. At grid-of-dancers scale duplication would balloon
309
- * the keyframe-blocks table past its page-table limits.
310
- *
311
- * Low-level `add_curve` deliberately stays uncached — every call
312
- * uploads — so existing callers that pre-build curves with
313
- * distinct IDs (e.g. a pre-built track pool)
314
- * keep their behavior.
315
- *
316
- * @type {WeakMap<AnimationCurve, number>}
317
- */
318
- #curve_id_cache = new WeakMap();
319
-
320
- /**
321
- * The curves uploaded so far, by what they are rather than which instance they were: the
322
- * `hash()` of a curve's keys, to every uploaded curve with that hash and its GPU id.
323
- *
324
- * An instance cache alone shares nothing between two parses of one file, and a character
325
- * instanced a few hundred times is a few hundred parses — each with its own instances of the
326
- * same keys, filling the keyframe-block table with copies of one dance until its page limit
327
- * refuses the next dancer. Curves are read-only once uploaded and nothing here releases them,
328
- * so equal content can safely mean one upload.
329
- *
330
- * A hash bucket is a list because a hash is not an identity: a curve is only shared with one
331
- * whose every key `equals` its own, tangents included.
332
- *
333
- * @type {Map<number, {curve: AnimationCurve, id: number}[]>}
334
- */
335
- #curve_id_by_content = new Map();
336
-
337
- /**
338
- * Cache of track-id-on-GPU keyed by `(curve_id_x, curve_id_y,
339
- * curve_id_z, curve_id_w, mask)`. With curve dedup already
340
- * sharing curve IDs across clips, two channels in different
341
- * clips that bind to the same (target_property, curves) end up
342
- * with identical track keys — and we can share the track row
343
- * too. Each curve_id slot is `-1` when the corresponding channel
344
- * is absent.
345
- *
346
- * Keys are joined into a string for Map lookup; the join cost is
347
- * negligible compared to the GPU upload it avoids.
348
- *
349
- * @type {Map<string, number>}
350
- */
351
- #track_id_cache = new Map();
352
-
353
- /**
354
- * Skins registered via {@link register_skin} whose joint-block
355
- * chain hasn't been built yet because one or more joint Node3Ds
356
- * lacks an `id_mapping` row. The skin's GPU table row exists
357
- * (placeholder `joint_block_head = NULL`, no joints driving the
358
- * prep pass yet), and state mutators / unregister already work
359
- * — but the deformation only kicks in once {@link update}
360
- * successfully drains the entry.
361
- *
362
- * Each entry: `skin_id`, the CPU {@link Skin} (whose `joints`
363
- * array drives the chain build), the `skin_matrix_offset`
364
- * reserved at register time, and the `inverse_bind_matrices`
365
- * typed array.
366
- *
367
- * @type {Array<{
368
- * skin_id: number,
369
- * skin: Skin,
370
- * skin_matrix_offset: number,
371
- * inverse_bind_matrices: Float32Array,
372
- * }>}
373
- */
374
- #pending_skins = [];
375
-
376
- /**
377
- * Clips registered via {@link register_clip} whose bindings
378
- * chain hasn't been built yet because one or more channel
379
- * targets lacks an `id_mapping` row. The clip's GPU table row
380
- * exists (placeholder `bound_track_group_head = NULL`, no
381
- * tracks driving the apply pass yet); time/state mutators and
382
- * unregister already work.
383
- *
384
- * Each entry: `clip_id` plus the per-channel `(track_id,
385
- * target, property)` triples — the curves and tracks were
386
- * interned at register time so only the (target → instance row)
387
- * resolution is deferred.
388
- *
389
- * @type {Array<{
390
- * clip_id: number,
391
- * channels: Array<{ track: number, target: Node3D, property: number }>,
392
- * }>}
393
- */
394
- #pending_clips = [];
395
-
396
- /**
397
- * @param {GPUDevice} device
398
- * @param {string} label
399
- * @param {GPUSceneContext} scene_context
400
- */
401
- constructor(device, label, scene_context) {
402
- assert.defined(device, 'device');
403
- assert.defined(scene_context, 'scene_context');
404
-
405
- this.#device = device;
406
- this.#label = label;
407
- this.#scene_context = scene_context;
408
-
409
- this.#database = new GPUDatabase({
410
- device,
411
- definition: ANIMATION_DATABASE_SPEC,
412
- label: `${label}/Database`,
413
- });
414
- }
415
-
416
- /**
417
- * @returns {GPUDatabase}
418
- */
419
- get database() {
420
- return this.#database;
421
- }
422
-
423
- /**
424
- * Per-frame skinning-matrix scratch buffer for the *current*
425
- * frame. `null` until the first skin is registered. The
426
- * skin-matrix-prep pass writes to it; the skinning compute pass
427
- * reads from it to deform current-frame positions. Address `j`
428
- * of skin `s` is at matrix index `s.skin_matrix_offset + j`.
429
- *
430
- * The buffer reference is invalidated on grow AND flips role
431
- * every frame as part of the ping-pong with
432
- * {@link prev_skin_matrices_buffer} — re-fetch each frame.
433
- *
434
- * @returns {GPUBuffer|null}
435
- */
436
- get skin_matrices_buffer() {
437
- return this.#skin_matrices_buffers[this.#skin_matrices_current_index];
438
- }
439
-
440
- /**
441
- * Previous-frame counterpart of {@link skin_matrices_buffer} —
442
- * holds the matrices the previous frame's prep dispatch wrote.
443
- * Read by the skinning compute pass to produce per-vertex
444
- * `position_prev` data for the velocity post-pass.
445
- *
446
- * `null` until the first skin is registered. Flips role with
447
- * {@link skin_matrices_buffer} every frame.
448
- *
449
- * @returns {GPUBuffer|null}
450
- */
451
- get prev_skin_matrices_buffer() {
452
- return this.#skin_matrices_buffers[1 - this.#skin_matrices_current_index];
453
- }
454
-
455
- /**
456
- * Number of matrices currently in use (= sum of `joint_count` over
457
- * registered skins). Independent of buffer capacity, which is at
458
- * least this big.
459
- *
460
- * @returns {number}
461
- */
462
- get skin_matrix_count() {
463
- return this.#next_skin_matrix_offset;
464
- }
465
-
466
- /**
467
- * Pose-accumulator scratch buffer (per-Node3D-row weighted
468
- * contribution slots). `null` until {@link ensure_pose_accumulator_capacity}
469
- * is called for the first time.
470
- *
471
- * The buffer reference is invalidated on grow — re-fetch each
472
- * frame after {@link ensure_pose_accumulator_capacity} rather
473
- * than caching across frames.
474
- *
475
- * @returns {GPUBuffer|null}
476
- */
477
- get pose_accumulator_buffer() {
478
- return this.#pose_accumulator_buffer;
479
- }
480
-
481
- /**
482
- * Ensure the pose-accumulator scratch buffer can address at least
483
- * `row_count` `transforms_table` rows. Grows by doubling from the
484
- * initial capacity; idempotent when already big enough. Called
485
- * by the per-frame dispatch driver after `scene.build` has
486
- * stabilised the transforms-table size for the frame.
487
- *
488
- * Grow destroys the previous GPU buffer; resolve always leaves the
489
- * accumulator zeroed at the end of each frame, so there's nothing
490
- * to copy across the realloc.
491
- *
492
- * @param {number} row_count
493
- */
494
- ensure_pose_accumulator_capacity(row_count) {
495
- assert.isNonNegativeInteger(row_count, 'row_count');
496
-
497
- // Existing buffer fits the request → nothing to do. The null
498
- // check guarantees the very first call always allocates,
499
- // even when `row_count == 0` (e.g. before any scene content
500
- // has been built): downstream binding paths assume the
501
- // buffer reference is non-null whenever the apply pass runs.
502
- if (this.#pose_accumulator_buffer !== null && row_count <= this.#pose_accumulator_capacity) {
503
- return;
504
- }
505
-
506
- let new_capacity = Math.max(this.#pose_accumulator_capacity, POSE_ACCUMULATOR_INITIAL_CAPACITY);
507
- while (new_capacity < row_count) {
508
- new_capacity *= 2;
509
- }
510
-
511
- if (this.#pose_accumulator_buffer !== null) {
512
- this.#pose_accumulator_buffer.destroy();
513
- }
514
-
515
- this.#pose_accumulator_buffer = this.#device.createBuffer({
516
- label: `${this.#label}/PoseAccumulator`,
517
- size: new_capacity * POSE_ACCUMULATOR_SLOT_BYTES,
518
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
519
- });
520
- this.#pose_accumulator_capacity = new_capacity;
521
- }
522
-
523
- /**
524
- * Read back a skin's record from the database's CPU shadow.
525
- *
526
- * @param {number} skin_id
527
- * @returns {{joint_block_head: number, joint_count: number}}
528
- */
529
- get_skin(skin_id) {
530
- assert.isNonNegativeInteger(skin_id, 'skin_id');
531
- return this.#database.get('animation_skins').get(skin_id);
532
- }
533
-
534
- /**
535
- * Base address of the skin's matrix range in the shared
536
- * `skin_matrices` scratch buffer — matrix `j` of the skin lives
537
- * at `skin_matrices[get_skin_matrix_offset(skin_id) + j]`.
538
- *
539
- * Tracked CPU-side rather than on the skin record because the
540
- * value is only ever consumed by the skinning-binding upload
541
- * path; the prep shader uses the per-block absolute offset
542
- * (cheaper than a per-thread skin lookup for that hot path).
543
- *
544
- * @param {number} skin_id
545
- * @returns {number}
546
- */
547
- get_skin_matrix_offset(skin_id) {
548
- assert.isNonNegativeInteger(skin_id, 'skin_id');
549
- const offset = this.#skin_matrix_offsets.get(skin_id);
550
- assert.defined(offset, `unknown skin_id ${skin_id}`);
551
- return offset;
552
- }
553
-
554
- /**
555
- * Add an animation curve. Keyframes are split into linked-list blocks
556
- * of up to {@link KEYFRAMES_PER_BLOCK} entries each; the curve header
557
- * points at the head block, and each block's `next` field chains
558
- * forward.
559
- *
560
- * The curve is a cubic Hermite spline — step, linear, and arbitrary
561
- * cubic shapes are all expressible by choosing tangents on each keyframe.
562
- *
563
- * @param {AnimationCurve} curve
564
- * @returns {number} curve id (index into the `animation_curves` table)
565
- */
566
- add_curve(curve) {
567
- assert.defined(curve, 'curve');
568
- assert.equal(curve.isAnimationCurve, true, 'curve.isAnimationCurve !== true');
569
- assert.greaterThanOrEqual(curve.length, 1, 'curve must have at least one keyframe');
570
-
571
- const keys = curve.keys;
572
- const blocks_table = this.#database.get('animation_keyframe_blocks');
573
-
574
- const head_block_id = add_linked_chain(
575
- blocks_table,
576
- keys,
577
- KEYFRAMES_PER_BLOCK,
578
- (start, count, next) => {
579
- const keyframes = new Array(KEYFRAMES_PER_BLOCK);
580
- for (let j = 0; j < KEYFRAMES_PER_BLOCK; j++) {
581
- keyframes[j] = j < count ? keys[start + j] : ZERO_KEYFRAME;
582
- }
583
- return { keyframes, count, next };
584
- }
585
- );
586
-
587
- const curves_table = this.#database.get('animation_curves');
588
- return curves_table.add({
589
- kind: 0,
590
- keyframe_block_head: head_block_id,
591
- keyframe_count: keys.length,
592
- time_min: curve.start_time,
593
- time_max: curve.end_time,
594
- });
595
- }
596
-
597
- /**
598
- * Add a track. Channels are addressed by name (`x`, `y`, `z`, `w`) — the
599
- * provided curves go into the matching slots of the track's `vec4f`
600
- * output. Omitted channels are masked off and read as 0.
601
- *
602
- * The slot meaning is property-specific: for `MeshTranslation`, x/y/z
603
- * are the translation components; for `MeshRotation` they're the
604
- * quaternion's x/y/z/w. The track's mask travels with the evaluated
605
- * value to the apply step, which uses it to decide which destinations
606
- * to overwrite.
607
- *
608
- * @param {{ x?: number, y?: number, z?: number, w?: number }} channels
609
- * curve ids per channel; pass at least one
610
- * @returns {number} track id (index into the `animation_tracks` table)
611
- */
612
- add_track(channels) {
613
- assert.defined(channels, 'channels');
614
-
615
- const cx = channels.x;
616
- const cy = channels.y;
617
- const cz = channels.z;
618
- const cw = channels.w;
619
-
620
- let mask = 0;
621
- if (cx !== undefined) mask |= 0x1;
622
- if (cy !== undefined) mask |= 0x2;
623
- if (cz !== undefined) mask |= 0x4;
624
- if (cw !== undefined) mask |= 0x8;
625
-
626
- assert.notEqual(mask, 0, 'track must have at least one channel');
627
-
628
- const curves = [
629
- cx ?? 0,
630
- cy ?? 0,
631
- cz ?? 0,
632
- cw ?? 0,
633
- ];
634
-
635
- const table = this.#database.get('animation_tracks');
636
- return table.add({ mask, curves });
637
- }
638
-
639
- /**
640
- * Create a bound clip — a group of bound tracks that share a single
641
- * `time`. Bindings are split into linked-list group nodes of up to
642
- * {@link TRACKS_PER_GROUP_NODE} each; the clip points at the head
643
- * node and each node back-references the clip so the apply shader
644
- * (one thread per group node) can fetch the clip without walking the
645
- * chain from the clip end.
646
- *
647
- * Tracks may be authored on a clock that doesn't align with
648
- * `[time_start, time_end]` — their curves are clamped to their own
649
- * `[time_min, time_max]` during evaluation.
650
- *
651
- * Defaults: `time = 0`, `time_start = 0`, `time_end = 0`,
652
- * `playback_rate = 1`, `playback_weight = 1`, `flags = 0` (not
653
- * playing, not looping). The clip is inert at creation — call
654
- * {@link start} to begin GPU-side time advancement.
655
- *
656
- * @param {{
657
- * time?: number,
658
- * time_start?: number,
659
- * time_end?: number,
660
- * bindings: Array<{ track: number, instance: number, property: number }>,
661
- * }} clip
662
- * @returns {number} clip id (index into the `animation_clips` table)
663
- */
664
- add_clip(clip) {
665
- assert.defined(clip, 'clip');
666
- assert.isArray(clip.bindings, 'clip.bindings');
667
- assert.greaterThanOrEqual(clip.bindings.length, 1, 'clip must have at least one binding');
668
-
669
- const time = clip.time ?? 0;
670
- const time_start = clip.time_start ?? 0;
671
- const time_end = clip.time_end ?? 0;
672
- const bindings = clip.bindings;
673
-
674
- const clips_table = this.#database.get('animation_clips');
675
- const groups_table = this.#database.get('animation_bound_track_groups');
676
-
677
- // Reserve the clip slot up front so group nodes can back-reference
678
- // it via `clip`. The head pointer is patched in once the chain is
679
- // built (we don't know its first id until we've allocated every
680
- // group node).
681
- const clip_id = clips_table.add({
682
- time,
683
- time_start,
684
- time_end,
685
- bound_track_group_head: ANIMATION_BOUND_TRACK_GROUP_NULL,
686
- playback_rate: 1,
687
- flags: 0,
688
- playback_weight: 1,
689
- });
690
-
691
- const head_group_id = add_linked_chain(
692
- groups_table,
693
- bindings,
694
- TRACKS_PER_GROUP_NODE,
695
- (start, count, next) => {
696
- const inline_bindings = new Array(TRACKS_PER_GROUP_NODE);
697
- for (let j = 0; j < TRACKS_PER_GROUP_NODE; j++) {
698
- if (j < count) {
699
- const b = bindings[start + j];
700
-
701
- assert.isNonNegativeInteger(b.track, `clip.bindings[${start + j}].track`);
702
- assert.isNonNegativeInteger(b.instance, `clip.bindings[${start + j}].instance`);
703
- assert.isNonNegativeInteger(b.property, `clip.bindings[${start + j}].property`);
704
-
705
- inline_bindings[j] = b;
706
- } else {
707
- inline_bindings[j] = ZERO_BINDING;
708
- }
709
- }
710
- return { bindings: inline_bindings, count, next, clip: clip_id };
711
- }
712
- );
713
-
714
- // Patch the head pointer now that the chain exists. Re-`set` the
715
- // whole record because the table API doesn't expose per-field
716
- // updates.
717
- clips_table.set(clip_id, {
718
- time,
719
- time_start,
720
- time_end,
721
- bound_track_group_head: head_group_id,
722
- playback_rate: 1,
723
- flags: 0,
724
- playback_weight: 1,
725
- });
726
-
727
- return clip_id;
728
- }
729
-
730
- /**
731
- * Overwrite a single field of a clip's record by reading the current
732
- * record back from the CPU shadow and re-`set`ting with one field
733
- * changed. The table API doesn't expose per-field updates, so we
734
- * round-trip through a full record; the upload-buffer dedupe
735
- * collapses the resulting per-frame write into a single GPU touch
736
- * when nothing else changed.
737
- *
738
- * @param {number} clip_id
739
- * @param {string} field
740
- * @param {number} value
741
- */
742
- #patch_clip_field(clip_id, field, value) {
743
- assert.isNonNegativeInteger(clip_id, 'clip_id');
744
-
745
- const clips_table = this.#database.get('animation_clips');
746
- const current = clips_table.get(clip_id);
747
-
748
- clips_table.set(clip_id, {
749
- time: current.time,
750
- time_start: current.time_start,
751
- time_end: current.time_end,
752
- bound_track_group_head: current.bound_track_group_head,
753
- playback_rate: current.playback_rate,
754
- flags: current.flags,
755
- playback_weight: current.playback_weight,
756
- [field]: value,
757
- });
758
- }
759
-
760
- /**
761
- * Direct override of a clip's `time`. Bypasses {@link start} /
762
- * {@link stop} — `time` is written even if the clip is paused, and
763
- * the {@link AnimationClipFlags.Playing} bit is left untouched.
764
- * The host typically uses this to scrub or rewind; GPU-driven
765
- * playback advances time on its own via the tick shader.
766
- *
767
- * TODO: race between `start()` and `set_time()` is non-atomic — if
768
- * a caller does `start(id); set_time(id, phase)` and the next GPU
769
- * tick fires before the page upload from `set_time` lands, the
770
- * tick advances from the old `time` for one frame. RIS would be
771
- * an atomic `start_at(id, time, ...)` that fuses both field
772
- * writes into one record set. For phase offsets the one-frame
773
- * skew is invisible; for scrub-and-resume UIs it can show.
774
- *
775
- * @param {number} clip_id
776
- * @param {number} time
777
- */
778
- set_time(clip_id, time) {
779
- assert.isNumber(time, 'time');
780
- this.#patch_clip_field(clip_id, 'time', time);
781
- }
782
-
783
- /**
784
- * Direct override of a clip's per-frame advance scalar. `1.0` =
785
- * realtime; `0.0` effectively pauses (independent of the
786
- * {@link AnimationClipFlags.Playing} bit); negative values play
787
- * in reverse.
788
- *
789
- * @param {number} clip_id
790
- * @param {number} rate
791
- */
792
- set_playback_rate(clip_id, rate) {
793
- assert.isNumber(rate, 'rate');
794
- this.#patch_clip_field(clip_id, 'playback_rate', rate);
795
- }
796
-
797
- /**
798
- * Override a clip's contribution weight into the pose accumulator.
799
- * `1.0` is full strength; `0.0` silences the clip without removing
800
- * its bindings; values in between blend it against other clips
801
- * targeting the same nodes. The apply shader pre-multiplies every
802
- * curve-evaluated value by this scalar before atomic-adding into
803
- * the accumulator.
804
- *
805
- * Normalized blends expect weights across all clips driving a
806
- * node to sum to `1`; additive layers use weights that don't sum
807
- * to `1`. The system doesn't enforce either — host's call.
808
- *
809
- * @param {number} clip_id
810
- * @param {number} weight
811
- */
812
- set_playback_weight(clip_id, weight) {
813
- assert.isNumber(weight, 'weight');
814
- this.#patch_clip_field(clip_id, 'playback_weight', weight);
815
- }
816
-
817
- /**
818
- * Add (OR) one or more flag bits onto a clip's bitfield. Bits
819
- * already set stay set; other bits are untouched. Values come
820
- * from {@link AnimationClipFlags} and can be combined with `|`:
821
- * `set_flags(id, AnimationClipFlags.Loop | AnimationClipFlags.Playing)`.
822
- *
823
- * Counterpart {@link clear_flags} ANDs the bits out. Together
824
- * they cover the common cases without the overwrite footgun
825
- * the original version had (where setting Loop would silently
826
- * clear Playing).
827
- *
828
- * For toggling just the playing bit, {@link start} / {@link stop}
829
- * are equivalent shortcuts.
830
- *
831
- * @param {number} clip_id
832
- * @param {number} flags bits to set
833
- */
834
- set_flags(clip_id, flags) {
835
- assert.isNonNegativeInteger(flags, 'flags');
836
-
837
- const clips_table = this.#database.get('animation_clips');
838
- const current = clips_table.get(clip_id);
839
-
840
- this.#patch_clip_field(clip_id, 'flags', current.flags | flags);
841
- }
842
-
843
- /**
844
- * Clear (AND-NOT) one or more flag bits from a clip's bitfield.
845
- * Bits not present in `flags` are left untouched. Symmetric
846
- * with {@link set_flags}.
847
- *
848
- * @param {number} clip_id
849
- * @param {number} flags bits to clear
850
- */
851
- clear_flags(clip_id, flags) {
852
- assert.isNonNegativeInteger(flags, 'flags');
853
-
854
- const clips_table = this.#database.get('animation_clips');
855
- const current = clips_table.get(clip_id);
856
-
857
- this.#patch_clip_field(clip_id, 'flags', current.flags & ~flags);
858
- }
859
-
860
- /**
861
- * Begin GPU-side playback. Sets the {@link AnimationClipFlags.Playing}
862
- * bit; preserves every other flag bit, the current `time`, the
863
- * `playback_rate`, and so on. Calling {@link stop} and then `start`
864
- * back-to-back should feel like a no-op modulo at most one tick's
865
- * worth of advance.
866
- *
867
- * Idempotent: starting an already-playing clip is a no-op (the
868
- * upload-buffer dedupe collapses the write).
869
- *
870
- * @param {number} clip_id
871
- */
872
- start(clip_id) {
873
- assert.isNonNegativeInteger(clip_id, 'clip_id');
874
-
875
- const clips_table = this.#database.get('animation_clips');
876
- const current = clips_table.get(clip_id);
877
-
878
- this.#patch_clip_field(clip_id, 'flags', current.flags | AnimationClipFlags.Playing);
879
- }
880
-
881
- /**
882
- * Pause GPU-side playback. Clears the {@link AnimationClipFlags.Playing}
883
- * bit; preserves every other flag bit and the current `time`.
884
- * Symmetric with {@link start} — a stop/start pair leaves the clip
885
- * where it was (give or take one tick of GPU time loss).
886
- *
887
- * Idempotent: stopping an already-stopped clip is a no-op.
888
- *
889
- * @param {number} clip_id
890
- */
891
- stop(clip_id) {
892
- assert.isNonNegativeInteger(clip_id, 'clip_id');
893
-
894
- const clips_table = this.#database.get('animation_clips');
895
- const current = clips_table.get(clip_id);
896
-
897
- this.#patch_clip_field(clip_id, 'flags', current.flags & ~AnimationClipFlags.Playing);
898
- }
899
-
900
- /**
901
- * Register a skin: an ordered list of joints, each pinning a
902
- * Node3D row that drives the joint plus the inverse-bind matrix
903
- * that takes vertex positions from mesh space into the joint's
904
- * bind-pose local space. The skin reserves a contiguous range of
905
- * `joints.length` matrices in the {@link #skin_matrices_buffer}
906
- * scratch buffer; the prep pass populates that range each frame
907
- * with `node.global × inverse_bind` for every joint.
908
- *
909
- * The skin record carries no mesh-world factor. The rasterizer
910
- * applies `mesh.transform_global` to the skinned clone vertex
911
- * via the standard mesh transform path
912
- * ({@link chunk_mesh_resolve_vertex_position_world}), so adding
913
- * an `inverse(mesh.global)` factor in the skin matrix would
914
- * double-cancel against that re-application and shrink the rig
915
- * back to mesh-local coordinates rendered as world.
916
- *
917
- * Joints are split into linked-list blocks of up to
918
- * {@link JOINTS_PER_SKIN_BLOCK} each — same chain pattern used for
919
- * bound-track groups and keyframe blocks. Each block records the
920
- * absolute write offset for its slots, so the prep shader's
921
- * per-thread work is one block load + a bounded inner loop with no
922
- * indirection back through the skin record.
923
- *
924
- * @param {{
925
- * joints: Array<{ node: number, inverse_bind: Float32Array | number[] }>,
926
- * }} skin
927
- * @returns {number} skin id (index into the `animation_skins` table)
928
- */
929
- add_skin(skin) {
930
- assert.defined(skin, 'skin');
931
- assert.isArray(skin.joints, 'skin.joints');
932
- assert.greaterThanOrEqual(skin.joints.length, 1, 'skin must have at least one joint');
933
-
934
- const joints = skin.joints;
935
- const joint_count = joints.length;
936
-
937
- // Reserve the matrix range. Each block bakes in its absolute
938
- // slot offset (skin_base + chain_position * JOINTS_PER_SKIN_BLOCK)
939
- // so the prep shader writes directly without dereferencing
940
- // the skin row for the offset; the base is also stashed on
941
- // the CPU map so the skinning-binding upload can look it up
942
- // without reading the GPU back.
943
- const skin_matrix_offset = this.#next_skin_matrix_offset;
944
- this.#next_skin_matrix_offset += joint_count;
945
- this.#ensure_skin_matrices_capacity(this.#next_skin_matrix_offset);
946
-
947
- const skins_table = this.#database.get('animation_skins');
948
- const blocks_table = this.#database.get('animation_skin_joint_blocks');
949
-
950
- // Reserve the skin row up front. The head pointer is patched
951
- // in once the chain exists. NULL placeholder — 0 is a valid
952
- // block row id (the first allocation in an empty table).
953
- const skin_id = skins_table.add({
954
- joint_block_head: ANIMATION_SKIN_JOINT_BLOCK_NULL,
955
- joint_count,
956
- });
957
- this.#skin_matrix_offsets.set(skin_id, skin_matrix_offset);
958
-
959
- const head_block_id = add_linked_chain(
960
- blocks_table,
961
- joints,
962
- JOINTS_PER_SKIN_BLOCK,
963
- (start, count, next) => {
964
- const inline_joints = new Array(JOINTS_PER_SKIN_BLOCK);
965
- for (let j = 0; j < JOINTS_PER_SKIN_BLOCK; j++) {
966
- if (j < count) {
967
- const joint = joints[start + j];
968
-
969
- assert.isNonNegativeInteger(joint.node, `skin.joints[${start + j}].node`);
970
- assert.defined(joint.inverse_bind, `skin.joints[${start + j}].inverse_bind`);
971
-
972
- inline_joints[j] = joint;
973
- } else {
974
- inline_joints[j] = ZERO_SKIN_JOINT;
975
- }
976
- }
977
- return {
978
- joints: inline_joints,
979
- count,
980
- next,
981
- skin_matrix_offset: skin_matrix_offset + start,
982
- };
983
- }
984
- );
985
-
986
- // Patch the head pointer now that the chain exists. Re-`set`
987
- // the whole record because the table API doesn't expose
988
- // per-field updates.
989
- skins_table.set(skin_id, {
990
- joint_block_head: head_block_id,
991
- joint_count,
992
- });
993
-
994
- return skin_id;
995
- }
996
-
997
- /**
998
- * High-level skin registration: consumes a {@link Skin} (CPU data
999
- * with Node3D joint references and a flat inverse-bind matrix
1000
- * array), allocates a GPU skin record, and binds every entry in
1001
- * `skin.meshes` through the skinning context.
1002
- *
1003
- * **Eventual consistency.** The `skin_id` is allocated and
1004
- * returned synchronously — callers can immediately pass it to
1005
- * state mutators or {@link unregister_skin}. If any joint's
1006
- * Node3D hasn't been added to a scene yet (no `id_mapping` row),
1007
- * the joint-block chain that drives the deformation is queued
1008
- * and built on the next {@link update} that sees all joints
1009
- * resolved. The skin's table row uses a placeholder
1010
- * `joint_block_head` until then — observationally indistinguishable
1011
- * from "skin with no joints driving it" (no deformation runs).
1012
- *
1013
- * Mesh→skin bindings register immediately via
1014
- * {@link GPUMeshSkinningContext#bind}; the skinning context
1015
- * already tolerates pre-scene-build registration. Each mesh also
1016
- * gets its {@link SkinnedMesh#skin} reference and a fresh set of
1017
- * bounds off it, synchronously — that part does not wait for
1018
- * `id_mapping`, because it reads the joints' `transform_global`
1019
- * rather than any GPU row.
1020
- *
1021
- * @param {Skin} skin
1022
- * @returns {number} skin id (index into the `animation_skins` table)
1023
- */
1024
- register_skin(skin) {
1025
- assert.defined(skin, 'skin');
1026
- assert.equal(skin.isSkin, true, 'skin.isSkin !== true');
1027
- assert.greaterThanOrEqual(skin.meshes.length, 1, 'skin.meshes must contain at least one mesh');
1028
-
1029
- const joint_count = skin.joints.length;
1030
- const inverse_bind_matrices = skin.inverse_bind_matrices;
1031
-
1032
- // Reserve the matrix range up front — block records carry
1033
- // their absolute slot offset, so the prep shader writes
1034
- // directly. The base also lands on #skin_matrix_offsets so
1035
- // skinning-binding upload can look it up.
1036
- const skin_matrix_offset = this.#next_skin_matrix_offset;
1037
- this.#next_skin_matrix_offset += joint_count;
1038
- this.#ensure_skin_matrices_capacity(this.#next_skin_matrix_offset);
1039
-
1040
- // Reserve the skin row immediately with a placeholder head
1041
- // pointer. State mutators and the public id are usable from
1042
- // this point on; the joint chain gets patched in once
1043
- // id_mapping has every joint (possibly synchronously below,
1044
- // possibly deferred via the pending queue).
1045
- const skins_table = this.#database.get('animation_skins');
1046
- const skin_id = skins_table.add({
1047
- // NULL placeholder — 0 is a valid block row id
1048
- joint_block_head: ANIMATION_SKIN_JOINT_BLOCK_NULL,
1049
- joint_count,
1050
- });
1051
- this.#skin_matrix_offsets.set(skin_id, skin_matrix_offset);
1052
-
1053
- // Bind every mesh into the skinning context. Idempotent and
1054
- // pre-build tolerant — the binding upload defers until
1055
- // id_mapping is ready.
1056
- //
1057
- // Each mesh also takes a reference to the CPU-side skin and
1058
- // re-derives its bounds off it. From here on the skin's
1059
- // formula is what places the mesh's vertices, and `Mesh`'s
1060
- // composition — rest-pose geometry under `transform_global` —
1061
- // is the wrong one, because the skinning pass cancels that
1062
- // transform back out (see {@link SkinnedMesh#updateBoundsBasic}).
1063
- // Nothing else would notice: bounds are refreshed by
1064
- // `updateMatrices`, and a character does not move again just
1065
- // because it finished loading.
1066
- // Before any of that: put the meshes into the frame this skin actually deforms in. The
1067
- // skinning pass writes each clone's vertices in `inverse(mesh.global)` space, so
1068
- // `mesh.global` is that frame — and glTF says a skinned mesh's node transform is ignored,
1069
- // which makes the frame the engine's to choose rather than the exporter's to get right.
1070
- // Ahead of the bind loop because it moves the mesh, and the bounds published below have to
1071
- // be the ones that survive. See `SKINNING_FRAME_PLAN.md`.
1072
- this.#normalize_skin_frame(skin);
1073
-
1074
- this.#skins_by_id.set(skin_id, skin);
1075
-
1076
- for (const clip of this.#scaling_clips.values()) {
1077
- this.#report_scale_conflicts(skin, clip);
1078
- }
1079
-
1080
- const skinning = this.#scene_context.skinning;
1081
- const meshes = skin.meshes;
1082
- for (let i = 0; i < meshes.length; i++) {
1083
- const mesh = meshes[i];
1084
-
1085
- skinning.bind(mesh, skin_id);
1086
-
1087
- mesh.skin = skin;
1088
- mesh.updateBoundsBasic();
1089
-
1090
- // The row the scene context published for this mesh holds the pre-skin bounds, and
1091
- // nothing about binding a skin looks like movement — `updateBoundsBasic` is not
1092
- // `updateMatrices` and does not touch the version the sync compares against. Without
1093
- // this the corrected box sits on the CPU object and never reaches the table.
1094
- mesh.needsUpdate = true;
1095
- }
1096
-
1097
- // Try to complete now. If id_mapping is ready for every
1098
- // joint, the chain gets built and patched in. Otherwise the
1099
- // skin lands on #pending_skins and the next update() drain
1100
- // retries.
1101
- const pending = {
1102
- skin_id,
1103
- skin,
1104
- skin_matrix_offset,
1105
- inverse_bind_matrices,
1106
- };
1107
- if (!this.#try_complete_skin(pending)) {
1108
- this.#pending_skins.push(pending);
1109
- }
1110
-
1111
- return skin_id;
1112
- }
1113
-
1114
- /**
1115
- * Put a skin's meshes into the frame it deforms in, and say so when they cannot be.
1116
- *
1117
- * The correction itself is {@link skin_normalize_mesh_frame}, which is where the argument lives.
1118
- * This is the reporting: an asset that renders wrong should say which asset and by how much, on
1119
- * the frame it is registered, rather than reaching a person as a screenshot of soup.
1120
- *
1121
- * @param {Skin} skin
1122
- */
1123
- #normalize_skin_frame(skin) {
1124
- const report = skin_normalize_mesh_frame(skin);
1125
-
1126
- // a glTF skin's `name` is optional and often absent; the mesh it deforms is what a person
1127
- // would recognise, and is what the asset's own hierarchy calls it
1128
- const label = skin.name !== '' ? skin.name : (skin.meshes[0]?.name ?? '<unnamed>');
1129
-
1130
- if (report.unwalked.length > 0) {
1131
- console.error(
1132
- `skin '${label}': ${report.unwalked.length} of its nodes have never been walked — `
1133
- + `'${report.unwalked[0].name}' among them — so their world transforms are the `
1134
- + `identity by default rather than by derivation. Call updateMatrices on the scene `
1135
- + `before registering. Nothing was moved; the deformation will be wrong.`
1136
- );
1137
-
1138
- return;
1139
- }
1140
-
1141
- if (report.frame_defect > 0.001) {
1142
- console.error(
1143
- `skin '${label}': no frame can deform it — `
1144
- + `inverse(frame) × joint.global × inverse_bind is ${report.frame_defect.toFixed(4)} `
1145
- + `away from rigid at its own bind pose, and a dual quaternion carries a rotation and `
1146
- + `a translation and nothing else. The rest pose has a non-uniform scale or a shear `
1147
- + `in it. Rendering will be wrong; see SKINNING_FRAME_PLAN.md §4.`
1148
- );
1149
-
1150
- return;
1151
- }
1152
-
1153
- if (report.residual > 0.001) {
1154
- console.error(
1155
- `skin '${label}': its mesh frame did not land — ${report.residual.toFixed(6)} `
1156
- + `left over. A correction carrying shear does not survive a Transform64's TRS `
1157
- + `components. Rendering will be wrong; see SKINNING_FRAME_PLAN.md §3.`
1158
- );
1159
-
1160
- return;
1161
- }
1162
-
1163
- for (const mesh of report.obstructed) {
1164
- console.warn(
1165
- `skin '${label}': mesh node '${mesh.name}' carries the skin's frame now, and its `
1166
- + `${mesh.children.length} child node(s) inherit it. Sockets belong on bones.`
1167
- );
1168
- }
1169
-
1170
- if (report.corrected > 0) {
1171
- // verbose rather than a warning: this is a legal asset shape, and a crowd of them would
1172
- // otherwise be a crowd of log lines
1173
- console.debug(
1174
- `skin '${label}': ${report.corrected} mesh frame(s) moved onto the skin's bind `
1175
- + `transform, absorbing ${report.absorbed.toFixed(4)} of non-rigidity the skinning `
1176
- + `pass could not have carried.`
1177
- );
1178
- }
1179
- }
1180
-
1181
- /**
1182
- * Say so when a clip scales a skin's joints out from under the frame it is deformed in.
1183
- *
1184
- * The frame {@link skin_normalize_mesh_frame} chooses is fixed at registration, and it is sound
1185
- * because the joints and the mesh share their scaling ancestors. A scale that reaches only some
1186
- * of them breaks that every frame it plays, on the GPU, where nothing can see it —
1187
- * {@link skin_clip_scale_conflicts} is that question asked off the clip's own curves instead.
1188
- *
1189
- * Nothing is refused: the clip is legal animation and the skin is a legal skin, and it is their
1190
- * combination the skinning pass cannot carry. Refusing either would be picking one to blame.
1191
- *
1192
- * @param {Skin} skin
1193
- * @param {ShadeAnimationClip} clip
1194
- */
1195
- #report_scale_conflicts(skin, clip) {
1196
- const conflicts = skin_clip_scale_conflicts(skin, clip);
1197
-
1198
- if (conflicts.length === 0) {
1199
- return;
1200
- }
1201
-
1202
- const skin_label = skin.name !== '' ? skin.name : (skin.meshes[0]?.name ?? '<unnamed>');
1203
-
1204
- const nodes = conflicts.map(conflict => conflict.node.name || '<unnamed>').join(', ');
1205
-
1206
- const uniform = conflicts.every(conflict => conflict.uniform);
1207
-
1208
- console.error(
1209
- `clip '${clip.name}' scales ${conflicts.length} node(s) of skin '${skin_label}' — `
1210
- + `${nodes} — which reach its joints but not its mesh, so the scale enters the joint `
1211
- + `matrices and not the frame they are folded against. A dual quaternion cannot carry `
1212
- + `it and no choice of frame fixes it: the deformation will be wrong while this clip `
1213
- + `plays. `
1214
- + (uniform
1215
- ? `The scale is uniform, which SKINNING_FRAME_PLAN.md §4 has a design for.`
1216
- : `The scale is non-uniform, which no dual quaternion holds — §4's linear-blend `
1217
- + `path or an import-time refusal.`)
1218
- );
1219
- }
1220
-
1221
- /**
1222
- * Attempt to build the joint-block chain for a pending skin and
1223
- * patch its head pointer onto the skin row. Returns `true` when
1224
- * every joint Node3D has resolved to a transforms-table row
1225
- * (and the chain was built); `false` when at least one joint is
1226
- * still missing.
1227
- *
1228
- * @param {{
1229
- * skin_id: number,
1230
- * skin: Skin,
1231
- * skin_matrix_offset: number,
1232
- * inverse_bind_matrices: Float32Array,
1233
- * }} pending
1234
- * @returns {boolean}
1235
- */
1236
- #try_complete_skin(pending) {
1237
- const id_mapping = this.#scene_context.id_mapping;
1238
- const joint_nodes = pending.skin.joints;
1239
- const joint_count = joint_nodes.length;
1240
- const inverse_bind_matrices = pending.inverse_bind_matrices;
1241
- const skin_matrix_offset = pending.skin_matrix_offset;
1242
- const skin_id = pending.skin_id;
1243
-
1244
- // All-or-nothing: a single missing row leaves the chain
1245
- // unbuilt — partial chains aren't valid (the prep shader
1246
- // walks every block).
1247
- const joints = new Array(joint_count);
1248
- for (let i = 0; i < joint_count; i++) {
1249
- const node = joint_nodes[i];
1250
- const row = id_mapping.get(node.id);
1251
- if (row === undefined) return false;
1252
-
1253
- joints[i] = {
1254
- node: row,
1255
- // Per-joint slice of the flat matrix array. The
1256
- // marshaller copies these bytes into the skin-joint
1257
- // block record; sub-array views are fine — they
1258
- // share the underlying buffer but write_struct_value
1259
- // reads by length, not by offset.
1260
- inverse_bind: inverse_bind_matrices.subarray(i * 16, (i + 1) * 16),
1261
- };
1262
- }
1263
-
1264
- const blocks_table = this.#database.get('animation_skin_joint_blocks');
1265
- const head_block_id = add_linked_chain(
1266
- blocks_table,
1267
- joints,
1268
- JOINTS_PER_SKIN_BLOCK,
1269
- (start, count, next) => {
1270
- const inline_joints = new Array(JOINTS_PER_SKIN_BLOCK);
1271
- for (let j = 0; j < JOINTS_PER_SKIN_BLOCK; j++) {
1272
- inline_joints[j] = j < count ? joints[start + j] : ZERO_SKIN_JOINT;
1273
- }
1274
- return {
1275
- joints: inline_joints,
1276
- count,
1277
- next,
1278
- skin_matrix_offset: skin_matrix_offset + start,
1279
- };
1280
- }
1281
- );
1282
-
1283
- // Patch the head pointer onto the already-allocated skin
1284
- // row. Read-modify-write so any other fields the user might
1285
- // have changed via mutators (none today, but future-proof)
1286
- // are preserved.
1287
- const skins_table = this.#database.get('animation_skins');
1288
- const current = skins_table.get(skin_id);
1289
- skins_table.set(skin_id, {
1290
- joint_block_head: head_block_id,
1291
- joint_count: current.joint_count,
1292
- });
1293
-
1294
- return true;
1295
- }
1296
-
1297
- /**
1298
- * High-level clip registration: consumes a {@link ShadeAnimationClip}
1299
- * (CPU data with Node3D targets and per-component curves), uploads
1300
- * curves and tracks, and creates the GPU clip record.
1301
- *
1302
- * **Eventual consistency.** The `clip_id` is allocated and
1303
- * returned synchronously — callers can immediately call
1304
- * {@link start} / {@link stop} / {@link set_time} /
1305
- * {@link set_playback_rate} / {@link set_flags} / {@link clear_flags}
1306
- * / {@link set_playback_weight} / {@link unregister_clip}. If
1307
- * any channel target's Node3D hasn't been added to a scene yet
1308
- * (no `id_mapping` row), the bindings chain that drives the
1309
- * apply pass is queued and built on the next {@link update}
1310
- * that sees all targets resolved. The clip's table row uses a
1311
- * placeholder `bound_track_group_head` until then — apply
1312
- * produces no contributions, so the clip is observationally
1313
- * inert (still subject to time advancement via the tick shader
1314
- * if `Playing` is set).
1315
- *
1316
- * Curves and tracks are interned synchronously since they're
1317
- * pure data — no `id_mapping` dependency.
1318
- *
1319
- * @param {ShadeAnimationClip} clip
1320
- * @returns {number} clip id
1321
- */
1322
- register_clip(clip) {
1323
- assert.defined(clip, 'clip');
1324
- assert.equal(clip.isShadeAnimationClip, true, 'clip.isShadeAnimationClip !== true');
1325
-
1326
- const channels = clip.channels;
1327
-
1328
- // Intern every channel's curves + track now. None of this
1329
- // depends on id_mapping — pure data uploads.
1330
- //
1331
- // Each curve goes through #intern_curve so two channels
1332
- // referencing the same AnimationCurve instance share one
1333
- // GPU upload. With 324 dancers retargeted at the same base
1334
- // clip this turns 324 × 195 channel uploads into 1 (the
1335
- // curves are read-only animation data — identical bytes
1336
- // wherever they're referenced).
1337
- const pending_channels = new Array(channels.length);
1338
- for (let i = 0; i < channels.length; i++) {
1339
- const channel = channels[i];
1340
- const curves = channel.curves;
1341
- const track_channels = {};
1342
- if (curves.x !== undefined) track_channels.x = this.#intern_curve(curves.x);
1343
- if (curves.y !== undefined) track_channels.y = this.#intern_curve(curves.y);
1344
- if (curves.z !== undefined) track_channels.z = this.#intern_curve(curves.z);
1345
- if (curves.w !== undefined) track_channels.w = this.#intern_curve(curves.w);
1346
-
1347
- pending_channels[i] = {
1348
- track: this.#intern_track(track_channels),
1349
- target: channel.target,
1350
- property: channel.property,
1351
- };
1352
- }
1353
-
1354
- // Reserve the clip row immediately with a placeholder head
1355
- // pointer. State mutators and the public id are usable from
1356
- // this point on; the bindings chain gets patched in once
1357
- // id_mapping has every target. `time` defaults to 0 (the
1358
- // playback offset relative to `time_start`; the apply
1359
- // shader does `effective_time = clip.time + clip.time_start`).
1360
- const clips_table = this.#database.get('animation_clips');
1361
- const clip_id = clips_table.add({
1362
- time: 0,
1363
- time_start: clip.start_time,
1364
- time_end: clip.end_time,
1365
- bound_track_group_head: ANIMATION_BOUND_TRACK_GROUP_NULL,
1366
- playback_rate: 1,
1367
- flags: 0,
1368
- playback_weight: 1,
1369
- });
1370
-
1371
- const pending = { clip_id, channels: pending_channels };
1372
- if (!this.#try_complete_clip(pending)) {
1373
- this.#pending_clips.push(pending);
1374
- }
1375
-
1376
- /*
1377
- Asked cheaply first: a clip with no scale channel at all — which is every clip a character
1378
- normally carries — is dismissed in one pass over its channels and remembered by nothing. Only
1379
- a clip that scales something is worth holding against the skins, now and as more arrive.
1380
- */
1381
- if (clip_scale_channels(clip).length > 0) {
1382
- this.#scaling_clips.set(clip_id, clip);
1383
-
1384
- for (const skin of this.#skins_by_id.values()) {
1385
- this.#report_scale_conflicts(skin, clip);
1386
- }
1387
- }
1388
-
1389
- return clip_id;
1390
- }
1391
-
1392
- /**
1393
- * Attempt to build the bindings chain for a pending clip and
1394
- * patch its head pointer onto the clip row. Returns `true` when
1395
- * every channel target has resolved to a transforms-table row
1396
- * (and the chain was built); `false` when at least one is still
1397
- * missing.
1398
- *
1399
- * @param {{
1400
- * clip_id: number,
1401
- * channels: Array<{ track: number, target: Node3D, property: number }>,
1402
- * }} pending
1403
- * @returns {boolean}
1404
- */
1405
- #try_complete_clip(pending) {
1406
- const id_mapping = this.#scene_context.id_mapping;
1407
- const channels = pending.channels;
1408
- const clip_id = pending.clip_id;
1409
-
1410
- const bindings = new Array(channels.length);
1411
- for (let i = 0; i < channels.length; i++) {
1412
- const ch = channels[i];
1413
- const instance = id_mapping.get(ch.target.id);
1414
- if (instance === undefined) return false;
1415
-
1416
- bindings[i] = {
1417
- track: ch.track,
1418
- instance,
1419
- property: ch.property,
1420
- };
1421
- }
1422
-
1423
- const groups_table = this.#database.get('animation_bound_track_groups');
1424
- const head_group_id = add_linked_chain(
1425
- groups_table,
1426
- bindings,
1427
- TRACKS_PER_GROUP_NODE,
1428
- (start, count, next) => {
1429
- const inline_bindings = new Array(TRACKS_PER_GROUP_NODE);
1430
- for (let j = 0; j < TRACKS_PER_GROUP_NODE; j++) {
1431
- inline_bindings[j] = j < count ? bindings[start + j] : ZERO_BINDING;
1432
- }
1433
- return { bindings: inline_bindings, count, next, clip: clip_id };
1434
- }
1435
- );
1436
-
1437
- // Patch the head pointer onto the already-allocated clip
1438
- // row. Read-modify-write preserves any user-driven state
1439
- // changes (set_time, set_flags, etc.) made between
1440
- // register_clip and the drain.
1441
- this.#patch_clip_field(clip_id, 'bound_track_group_head', head_group_id);
1442
-
1443
- return true;
1444
- }
1445
-
1446
- /**
1447
- * Drop a clip from the manager. Walks the bindings chain and
1448
- * removes every group-node + the clip row. Tolerates the clip
1449
- * still being pending (drains it from the queue without GPU
1450
- * touches besides the original row removal).
1451
- *
1452
- * Curves and tracks the clip referenced stay alive — they're
1453
- * shared via {@link #intern_curve} / {@link #intern_track} and
1454
- * may be in use by other clips. Removing them would need
1455
- * reference-counting the manager doesn't carry.
1456
- *
1457
- * Safe to call repeatedly on the same id (subsequent calls are
1458
- * no-ops once the row is gone).
1459
- *
1460
- * @param {number} clip_id
1461
- */
1462
- unregister_clip(clip_id) {
1463
- assert.isNonNegativeInteger(clip_id, 'clip_id');
1464
-
1465
- // Drop from pending queue if present (skip-and-keep-others
1466
- // pattern is cheaper than splice for the rare case of
1467
- // dozens-of-pending; if pending depth grows this can be
1468
- // revisited).
1469
- for (let i = this.#pending_clips.length - 1; i >= 0; i--) {
1470
- if (this.#pending_clips[i].clip_id === clip_id) {
1471
- this.#pending_clips.splice(i, 1);
1472
- }
1473
- }
1474
-
1475
- const clips_table = this.#database.get('animation_clips');
1476
- const groups_table = this.#database.get('animation_bound_track_groups');
1477
-
1478
- const clip_record = clips_table.get(clip_id);
1479
- if (clip_record === undefined) return;
1480
-
1481
- // Walk the bindings chain and remove every node. The chain
1482
- // may be empty (placeholder head NULL) if the clip is still
1483
- // pending or has been registered then drained but never
1484
- // completed; both cases skip the loop.
1485
- let block_id = clip_record.bound_track_group_head;
1486
- while (block_id !== ANIMATION_BOUND_TRACK_GROUP_NULL) {
1487
- const block = groups_table.get(block_id);
1488
- const next_id = block !== undefined ? block.next : ANIMATION_BOUND_TRACK_GROUP_NULL;
1489
- groups_table.remove(block_id);
1490
- block_id = next_id;
1491
- }
1492
-
1493
- clips_table.remove(clip_id);
1494
-
1495
- this.#scaling_clips.delete(clip_id);
1496
- }
1497
-
1498
- /**
1499
- * Drop a skin from the manager. Walks the joint-block chain and
1500
- * removes every block + the skin row, and tears down every
1501
- * skinning binding pointing at this skin (so the skinning
1502
- * context doesn't try to read the now-freed skin's matrix
1503
- * offset on its next upload).
1504
- *
1505
- * The skin's reserved matrix range stays allocated — compaction
1506
- * isn't supported in the MVP. Re-registering after an
1507
- * `unregister_skin` therefore costs new matrix slots; long-
1508
- * running spawn/despawn loops accumulate wasted slots.
1509
- *
1510
- * Tolerates the skin still being pending. Safe to call
1511
- * repeatedly on the same id.
1512
- *
1513
- * @param {number} skin_id
1514
- */
1515
- unregister_skin(skin_id) {
1516
- assert.isNonNegativeInteger(skin_id, 'skin_id');
1517
-
1518
- this.#skins_by_id.delete(skin_id);
1519
-
1520
- for (let i = this.#pending_skins.length - 1; i >= 0; i--) {
1521
- if (this.#pending_skins[i].skin_id === skin_id) {
1522
- this.#pending_skins.splice(i, 1);
1523
- }
1524
- }
1525
-
1526
- // Tear down skinning bindings first so the skinning
1527
- // context's next upload doesn't reference a freed skin row
1528
- // (its upload path looks up skin_matrix_offset via
1529
- // get_skin_matrix_offset, which would assert post-removal).
1530
- this.#scene_context.skinning.unbind_all_by_skin(skin_id);
1531
-
1532
- const skins_table = this.#database.get('animation_skins');
1533
- const blocks_table = this.#database.get('animation_skin_joint_blocks');
1534
-
1535
- const skin_record = skins_table.get(skin_id);
1536
- if (skin_record === undefined) {
1537
- this.#skin_matrix_offsets.delete(skin_id);
1538
- return;
1539
- }
1540
-
1541
- // Walk the joint-block chain, terminating on the NULL
1542
- // sentinel (also the placeholder for a not-yet-patched head).
1543
- // Note: 0 is a VALID block row id — the first single-block
1544
- // skin registered into an empty table gets head 0, and
1545
- // treating it as a placeholder would leak its block, leaving
1546
- // the prep shader processing it against a recycled
1547
- // transforms-table row every frame.
1548
- let block_id = skin_record.joint_block_head;
1549
- while (block_id !== ANIMATION_SKIN_JOINT_BLOCK_NULL) {
1550
- const block = blocks_table.get(block_id);
1551
- const next_id = block !== undefined ? block.next : ANIMATION_SKIN_JOINT_BLOCK_NULL;
1552
- blocks_table.remove(block_id);
1553
- block_id = next_id;
1554
- }
1555
-
1556
- skins_table.remove(skin_id);
1557
- this.#skin_matrix_offsets.delete(skin_id);
1558
- }
1559
-
1560
- /**
1561
- * Cached wrapper around {@link add_curve}: one upload per distinct curve, where distinct means
1562
- * distinct *content*.
1563
- *
1564
- * The instance cache answers first — the common case inside one clip, and free. Otherwise the
1565
- * curve's keys are hashed and held against every uploaded curve with that hash; a match on
1566
- * every key, tangents included, reuses that upload, and only a curve nobody has uploaded before
1567
- * goes through the low-level path. Either way the instance is remembered, so the next look-up
1568
- * of it is the free one.
1569
- *
1570
- * @param {AnimationCurve} curve
1571
- * @returns {number} curve id (index into the `animation_curves` table)
1572
- */
1573
- #intern_curve(curve) {
1574
- const cached = this.#curve_id_cache.get(curve);
1575
-
1576
- if (cached !== undefined) {
1577
- return cached;
1578
- }
1579
-
1580
- const hash = curve.hash();
1581
-
1582
- let bucket = this.#curve_id_by_content.get(hash);
1583
-
1584
- if (bucket === undefined) {
1585
- bucket = [];
1586
-
1587
- this.#curve_id_by_content.set(hash, bucket);
1588
- } else {
1589
- for (const entry of bucket) {
1590
- if (entry.curve.equals(curve)) {
1591
- this.#curve_id_cache.set(curve, entry.id);
1592
-
1593
- return entry.id;
1594
- }
1595
- }
1596
- }
1597
-
1598
- const id = this.add_curve(curve);
1599
-
1600
- this.#curve_id_cache.set(curve, id);
1601
-
1602
- bucket.push({ curve, id });
1603
-
1604
- return id;
1605
- }
1606
-
1607
- /**
1608
- * Cache-keyed wrapper around {@link add_track}. Two channels that
1609
- * end up with the same (x, y, z, w, mask) — typical when their
1610
- * source curves are shared via {@link #intern_curve} — collapse
1611
- * to one track row instead of one row per channel.
1612
- *
1613
- * @param {{ x?: number, y?: number, z?: number, w?: number }} channels
1614
- * @returns {number} track id
1615
- */
1616
- #intern_track(channels) {
1617
- // Pack into a deterministic string key. -1 sentinel for
1618
- // missing channels keeps the (x=undefined) and (x=0) cases
1619
- // distinct.
1620
- const cx = channels.x ?? -1;
1621
- const cy = channels.y ?? -1;
1622
- const cz = channels.z ?? -1;
1623
- const cw = channels.w ?? -1;
1624
- const key = `${cx},${cy},${cz},${cw}`;
1625
-
1626
- const cached = this.#track_id_cache.get(key);
1627
- if (cached !== undefined) {
1628
- return cached;
1629
- }
1630
- const id = this.add_track(channels);
1631
- this.#track_id_cache.set(key, id);
1632
- return id;
1633
- }
1634
-
1635
- /**
1636
- * Grow the skinning-matrix scratch buffer if `required_count`
1637
- * exceeds current capacity. Doubles on grow until the requirement
1638
- * fits. The buffer's contents are scratch (rewritten every frame by
1639
- * the prep pass), so growth doesn't need to copy old data — but it
1640
- * does invalidate the previous {@link #skin_matrices_buffer}
1641
- * reference, which is why callers re-fetch through the getter.
1642
- *
1643
- * @param {number} required_count number of matrices that must fit
1644
- */
1645
- #ensure_skin_matrices_capacity(required_count) {
1646
- if (required_count <= this.#skin_matrices_capacity) {
1647
- return;
1648
- }
1649
-
1650
- let new_capacity = Math.max(this.#skin_matrices_capacity, SKIN_MATRICES_INITIAL_CAPACITY);
1651
- while (new_capacity < required_count) {
1652
- new_capacity *= 2;
1653
- }
1654
-
1655
- // Grow both ping-pong buffers in lockstep — they always share
1656
- // capacity. After grow both are zero-initialised, so the next
1657
- // prep dispatch needs to seed "previous" from "current"
1658
- // (otherwise velocity would interpolate against zero
1659
- // matrices). The flag below tells dispatch_skin_matrix_prep
1660
- // to issue that copy.
1661
- for (let i = 0; i < 2; i++) {
1662
- if (this.#skin_matrices_buffers[i] !== null) {
1663
- this.#skin_matrices_buffers[i].destroy();
1664
- }
1665
- this.#skin_matrices_buffers[i] = this.#device.createBuffer({
1666
- label: `${this.#label}/SkinMatrices/${i}`,
1667
- size: new_capacity * SKIN_MATRIX_BYTES,
1668
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
1669
- });
1670
- }
1671
- this.#skin_matrices_capacity = new_capacity;
1672
- this.#first_frame_skin_matrices = true;
1673
- }
1674
-
1675
- /**
1676
- * Flush all pending uploads (curves, keyframe blocks, tracks, clips,
1677
- * bound-track groups, skins, skin joint blocks) into the GPU
1678
- * database. Call once per frame, before any shader that reads from
1679
- * `database.buffer`.
1680
- *
1681
- * Also drains the pending-registration queues: skins and clips
1682
- * whose dependencies (joint Node3D rows, channel-target rows)
1683
- * weren't ready at register time get one shot per `update` to
1684
- * complete. Once `id_mapping` has the missing rows, the
1685
- * deferred chain build runs and the registration becomes
1686
- * observationally complete — the next-frame `database.update`
1687
- * picks up the patched head pointer.
1688
- *
1689
- * Does not touch the skin-matrices ping-pong buffers — those
1690
- * are scratch populated each frame by
1691
- * {@link dispatch_skin_matrix_prep}, not host data.
1692
- *
1693
- * @param {ShadeGPUCommandContext} cmd_ctx
1694
- */
1695
- update(cmd_ctx) {
1696
- this.#drain_pending_skins();
1697
- this.#drain_pending_clips();
1698
- this.#database.update(cmd_ctx);
1699
- }
1700
-
1701
- #drain_pending_skins() {
1702
- // Walk back-to-front so successful completions can splice
1703
- // without disturbing the iteration's remaining indices.
1704
- for (let i = this.#pending_skins.length - 1; i >= 0; i--) {
1705
- if (this.#try_complete_skin(this.#pending_skins[i])) {
1706
- this.#pending_skins.splice(i, 1);
1707
- }
1708
- }
1709
- }
1710
-
1711
- #drain_pending_clips() {
1712
- for (let i = this.#pending_clips.length - 1; i >= 0; i--) {
1713
- if (this.#try_complete_clip(this.#pending_clips[i])) {
1714
- this.#pending_clips.splice(i, 1);
1715
- }
1716
- }
1717
- }
1718
-
1719
- /**
1720
- * Run the per-frame skin-matrix-prep dispatch and advance the
1721
- * ping-pong. Replaces the manual `shader_skin_matrix_prep.dispatch`
1722
- * the prototypes used to call — wrapping it here is how we get
1723
- * correct ping-pong behaviour across frames without leaking the
1724
- * buffer-role bookkeeping into every caller.
1725
- *
1726
- * No-op when no skins are registered (`group_count_x === 0`).
1727
- *
1728
- * Per-frame ordering:
1729
- * 1. Flip {@link #skin_matrices_current_index} — the buffer that
1730
- * was last frame's "current" is about to be overwritten with
1731
- * this frame's matrices; the other buffer (last frame's prev)
1732
- * becomes this frame's "previous" implicit data source for
1733
- * {@link prev_skin_matrices_buffer}.
1734
- * 2. Wait — clarify: we want last frame's "current" to BECOME
1735
- * this frame's "previous". So flipping current_index moves
1736
- * the role tag the correct way: what was current[i] becomes
1737
- * previous (= buffers[1 - i]), and current[1 - i] is the
1738
- * write target.
1739
- * 3. Dispatch the prep shader into the new current.
1740
- * 4. On the very first frame (or after a grow), copy current →
1741
- * previous so the first velocity reads see matching matrices
1742
- * and produce zero velocity.
1743
- *
1744
- * @param {ShadeGPUCommandContext} cmd_ctx
1745
- */
1746
- dispatch_skin_matrix_prep(cmd_ctx) {
1747
- const groups = this.#database
1748
- .get('animation_skin_joint_blocks')
1749
- .dispatch_group_count(SKIN_MATRIX_PREP_WORKGROUP_SIZE);
1750
- if (groups === 0) {
1751
- return;
1752
- }
1753
-
1754
- // Flip the ping-pong: what was "current" last frame is no
1755
- // longer current, but it still holds valid matrices — and
1756
- // through the role flip it becomes this frame's
1757
- // `prev_skin_matrices_buffer`. The buffer we're flipping TO
1758
- // (the new "current") held two-frames-old matrices, about to
1759
- // be overwritten.
1760
- this.#skin_matrices_current_index = 1 - this.#skin_matrices_current_index;
1761
-
1762
- const current = this.#skin_matrices_buffers[this.#skin_matrices_current_index];
1763
- const previous = this.#skin_matrices_buffers[1 - this.#skin_matrices_current_index];
1764
-
1765
- shader_skin_matrix_prep.dispatch({
1766
- encoder: cmd_ctx,
1767
- group_count_x: groups,
1768
- bindings: {
1769
- animation_database: this.#database.buffer,
1770
- scene_database: this.#scene_context.scene_database_buffer,
1771
- skin_matrices: current,
1772
- },
1773
- });
1774
-
1775
- // First-frame init (or post-grow): "previous" buffer is
1776
- // zero-initialised, which would have the velocity pass
1777
- // interpolate against the origin and produce huge nonsense
1778
- // on the first frame the skin appears. Seed prev from
1779
- // current — first velocity reads then yield zero, which is
1780
- // the right answer (no motion has happened yet).
1781
- if (this.#first_frame_skin_matrices) {
1782
- cmd_ctx.copyBufferToBuffer(
1783
- current, 0,
1784
- previous, 0,
1785
- current.size,
1786
- );
1787
- this.#first_frame_skin_matrices = false;
1788
- }
1789
- }
1790
-
1791
- /**
1792
- * Per-frame orchestrator: drains pending registrations and the
1793
- * database queue, runs the GPU tick (advances `clip.time`),
1794
- * applies tracks into the pose accumulator, resolves the
1795
- * accumulator into node TRS, propagates the transform
1796
- * hierarchy, preps skinning matrices, and runs the skinning
1797
- * compute pass that deforms cloned vertex regions in place.
1798
- *
1799
- * One call replaces the ~80-line per-frame dispatch block every
1800
- * consumer would otherwise duplicate; the shader identities,
1801
- * workgroup sizes, pose-accumulator sizing, and pass ordering
1802
- * are all internal.
1803
- *
1804
- * `dt_seconds` is wall-clock seconds since the last call (the
1805
- * caller measures via `performance.now()` or equivalent). It's
1806
- * clamped to {@link MAX_DT_SECONDS} to keep a stall from
1807
- * leaking multiple seconds into the next tick.
1808
- *
1809
- * Clip tick/apply/resolve gate on bound tracks; hierarchy propagation runs
1810
- * every frame, before skin matrix prep and skinning. CPU-driven nodes need it
1811
- * too: scene uploads seed global with the previous pose, and propagation
1812
- * composes the current one. On idle frames it advances prev_global so motion
1813
- * vectors settle to zero. Skin passes still gate on registered skins.
1814
- *
1815
- * @param {ShadeGPUCommandContext} cmd_ctx
1816
- * @param {number} dt_seconds wall-clock seconds since last call
1817
- */
1818
- tick(cmd_ctx, dt_seconds) {
1819
- assert.defined(cmd_ctx, 'cmd_ctx');
1820
- assert.isNumber(dt_seconds, 'dt_seconds');
1821
-
1822
- // Cap dt so a long stall doesn't fly the clip through dozens
1823
- // of loops or clamp it past the meaningful range.
1824
- const dt = Math.min(dt_seconds, MAX_DT_SECONDS);
1825
-
1826
- // Flush database + drain pending registrations.
1827
- this.update(cmd_ctx);
1828
-
1829
- // Only clip evaluation is conditional. Every scene needs current globals.
1830
- const apply_groups = this.#database
1831
- .get('animation_bound_track_groups')
1832
- .dispatch_group_count(ANIMATION_APPLY_WORKGROUP_SIZE);
1833
-
1834
- if (apply_groups > 0) {
1835
- this.#encode_clip_passes(cmd_ctx, dt, apply_groups);
1836
- } else {
1837
- // CPU uploads seed global with the previous pose. Even without clips,
1838
- // compose current globals and advance prev_global before skinning/rendering.
1839
- const graph = new FrameGraph("GPUAnimationManager/TransformHierarchy");
1840
- graph_scene_update_transform_hierarchy({ graph, scene: this.#scene_context });
1841
- cmd_ctx.encodeGraph(graph);
1842
- }
1843
-
1844
- // Skin matrix prep + skinning deformation. Both no-op when
1845
- // no skins are registered — and both run independent of the
1846
- // bound-track gate above: a registered skin whose joints
1847
- // are host-driven (no clips bound) must still be deformed
1848
- // each frame, otherwise it renders bind pose forever.
1849
- this.dispatch_skin_matrix_prep(cmd_ctx);
1850
- this.#scene_context.skinning.update(cmd_ctx);
1851
- }
1852
-
1853
- /**
1854
- * Encode the clip-driven passes: GPU tick (advances
1855
- * `clip.time`), track apply into the pose accumulator, pose
1856
- * resolve into node TRS, and transform-hierarchy propagation.
1857
- * Split out of {@link tick} so the skin passes there run
1858
- * independently of the bound-track early-out.
1859
- *
1860
- * @param {ShadeGPUCommandContext} cmd_ctx
1861
- * @param {number} dt clamped delta time, seconds
1862
- * @param {number} apply_groups dispatch group count for the
1863
- * apply pass — caller guarantees `> 0`
1864
- */
1865
- #encode_clip_passes(cmd_ctx, dt, apply_groups) {
1866
- // Tick: advance clip.time on the GPU.
1867
- const tick_groups = this.#database
1868
- .get('animation_clips')
1869
- .dispatch_group_count(ANIMATION_TICK_WORKGROUP_SIZE);
1870
- if (tick_groups > 0) {
1871
- shader_animation_tick.dispatch({
1872
- encoder: cmd_ctx,
1873
- group_count_x: tick_groups,
1874
- bindings: {
1875
- dt,
1876
- animation_database: this.#database.buffer,
1877
- },
1878
- });
1879
- }
1880
-
1881
- // Size the pose accumulator to cover every allocated row of
1882
- // the transforms table — apply writes by absolute row index,
1883
- // resolve iterates the table via page iterator.
1884
- const transforms_table = this.#scene_context.scene_database.get('transforms');
1885
- const resolve_groups = transforms_table.dispatch_group_count(
1886
- ANIMATION_POSE_RESOLVE_WORKGROUP_SIZE
1887
- );
1888
- this.ensure_pose_accumulator_capacity(transforms_table.element_capacity);
1889
-
1890
- // ── Frame graph: apply → pose resolve → hierarchy propagate
1891
- const graph = new FrameGraph("GPUAnimationManager/Tick");
1892
- const gr_animation_database = graph_import_buffer(
1893
- graph, this.#database.buffer, "animation_database"
1894
- );
1895
- const gr_scene_database = graph_import_buffer(
1896
- graph, this.#scene_context.scene_database_buffer, "scene_database"
1897
- );
1898
- const gr_materials = graph_import_buffer(
1899
- graph, this.#scene_context.materials.metadata_table.buffer, "materials"
1900
- );
1901
- const gr_pose_accumulator = graph_import_buffer(
1902
- graph, this.#pose_accumulator_buffer, "pose_accumulator"
1903
- );
1904
-
1905
- const apply_data = {};
1906
- const apply_pass = graph.add("animation/apply", apply_data, (data, resources, ctx) => {
1907
- shader_animation_apply.dispatch({
1908
- encoder: ctx.encoder,
1909
- group_count_x: apply_groups,
1910
- bindings: {
1911
- animation_database: resources.get(data.animation_database),
1912
- pose_accumulator: resources.get(data.pose_accumulator),
1913
- materials: resources.get(data.materials),
1914
- },
1915
- });
1916
- });
1917
- apply_data.animation_database = apply_pass.read(gr_animation_database);
1918
- apply_data.materials = apply_pass.write(apply_pass.read(gr_materials));
1919
- apply_data.pose_accumulator = apply_pass.write(apply_pass.read(gr_pose_accumulator));
1920
-
1921
- const resolve_data = {};
1922
- const resolve_pass = graph.add("animation/pose_resolve", resolve_data, (data, resources, ctx) => {
1923
- if (resolve_groups === 0) return;
1924
- shader_animation_pose_resolve.dispatch({
1925
- encoder: ctx.encoder,
1926
- group_count_x: resolve_groups,
1927
- bindings: {
1928
- scene_database: resources.get(data.scene_database),
1929
- pose_accumulator: resources.get(data.pose_accumulator),
1930
- },
1931
- });
1932
- });
1933
- resolve_data.scene_database = resolve_pass.write(resolve_pass.read(gr_scene_database));
1934
- resolve_data.pose_accumulator = resolve_pass.write(resolve_pass.read(apply_data.pose_accumulator));
1935
-
1936
- graph_scene_update_transform_hierarchy({
1937
- graph,
1938
- scene: this.#scene_context,
1939
- scene_database: resolve_data.scene_database,
1940
- });
1941
-
1942
- cmd_ctx.encodeGraph(graph);
1943
- }
1944
-
1945
- destroy() {
1946
- this.#database.destroy();
1947
- for (let i = 0; i < 2; i++) {
1948
- if (this.#skin_matrices_buffers[i] !== null) {
1949
- this.#skin_matrices_buffers[i].destroy();
1950
- this.#skin_matrices_buffers[i] = null;
1951
- }
1952
- }
1953
- if (this.#pose_accumulator_buffer !== null) {
1954
- this.#pose_accumulator_buffer.destroy();
1955
- this.#pose_accumulator_buffer = null;
1956
- }
1957
- }
1958
- }
1
+ import { assert } from "../../../core/assert.js";
2
+ import { FrameGraph } from "../../../engine/graphics/render/frame_graph/FrameGraph.js";
3
+ import { GPUDatabase } from "../buffer/table/GPUDatabase.js";
4
+ import {
5
+ graph_scene_update_transform_hierarchy
6
+ } from "../scene/hierarchy/graph_scene_update_transform_hierarchy.js";
7
+ import { graph_import_buffer } from "../shader/graph/graph_import_buffer.js";
8
+ import {
9
+ ANIMATION_BOUND_TRACK_GROUP_NULL,
10
+ TRACKS_PER_GROUP_NODE,
11
+ } from "./ANIMATION_BOUND_TRACK_GROUP_STRUCT.js";
12
+ import { ANIMATION_DATABASE_SPEC } from "./ANIMATION_DATABASE_SPEC.js";
13
+ import { AnimationClipFlags } from "./AnimationClipFlags.js";
14
+ import { KEYFRAMES_PER_BLOCK } from "./ANIMATION_KEYFRAME_BLOCK_STRUCT.js";
15
+ import {
16
+ ANIMATION_SKIN_JOINT_BLOCK_NULL,
17
+ JOINTS_PER_SKIN_BLOCK,
18
+ } from "./ANIMATION_SKIN_JOINT_BLOCK_STRUCT.js";
19
+ import { POSE_ACCUMULATOR_SLOT_BYTES } from "./POSE_ACCUMULATOR_LAYOUT.js";
20
+ import {
21
+ clip_scale_channels,
22
+ skin_clip_scale_conflicts
23
+ } from "./skin_clip_scale_conflicts.js";
24
+ import { skin_normalize_mesh_frame } from "./skin_normalize_mesh_frame.js";
25
+ import {
26
+ shader_animation_apply,
27
+ WORKGROUP_SIZE as ANIMATION_APPLY_WORKGROUP_SIZE,
28
+ } from "./shader_animation_apply.js";
29
+ import {
30
+ shader_animation_pose_resolve,
31
+ WORKGROUP_SIZE as ANIMATION_POSE_RESOLVE_WORKGROUP_SIZE,
32
+ } from "./shader_animation_pose_resolve.js";
33
+ import {
34
+ shader_animation_tick,
35
+ WORKGROUP_SIZE as ANIMATION_TICK_WORKGROUP_SIZE,
36
+ } from "./shader_animation_tick.js";
37
+ import {
38
+ shader_skin_matrix_prep,
39
+ WORKGROUP_SIZE as SKIN_MATRIX_PREP_WORKGROUP_SIZE,
40
+ } from "./skinning/shader_skin_matrix_prep.js";
41
+
42
+ /**
43
+ * @typedef {import("../../../engine/animation/curve/AnimationCurve.js").AnimationCurve} AnimationCurve
44
+ * @typedef {import("../../device/ShadeGPUCommandContext.js").ShadeGPUCommandContext} ShadeGPUCommandContext
45
+ * @typedef {import("../scene/GPUSceneContext.js").GPUSceneContext} GPUSceneContext
46
+ * @typedef {import("./ShadeAnimationClip.js").ShadeAnimationClip} ShadeAnimationClip
47
+ * @typedef {import("./Skin.js").Skin} Skin
48
+ */
49
+
50
+ /**
51
+ * Tail-slot placeholders for the fixed-size inline arrays in chain
52
+ * nodes. The shader's `count` guard means these bytes are never read,
53
+ * but the upload buffer doesn't zero unwritten ranges and
54
+ * `write_struct_value` skips undefined fields — so explicit zeros are
55
+ * the only way to keep tail bytes deterministic. Frozen so a stray
56
+ * mutation can't bleed into multiple slots that share the reference.
57
+ */
58
+ const ZERO_KEYFRAME = Object.freeze({ time: 0, value: 0, inTangent: 0, outTangent: 0 });
59
+ const ZERO_BINDING = Object.freeze({ track: 0, instance: 0, property: 0 });
60
+ // Object.freeze on a typed array with elements throws under V8
61
+ // ("Cannot freeze array buffer views with elements"), so we leave the
62
+ // Float32Array unfrozen — the surrounding placeholder object is still
63
+ // frozen to keep accidental field-level mutation out of bounds.
64
+ const ZERO_INVERSE_BIND = new Float32Array(16);
65
+ const ZERO_SKIN_JOINT = Object.freeze({ node: 0, inverse_bind: ZERO_INVERSE_BIND });
66
+
67
+ /**
68
+ * One skinning matrix is a `mat4x4f` — 16 f32s, 64 bytes. The
69
+ * `skin_matrices` scratch buffer holds one entry per (skin, joint) pair
70
+ * across every registered skin.
71
+ */
72
+ const SKIN_MATRIX_BYTES = 64;
73
+
74
+ /**
75
+ * Initial capacity of the `skin_matrices` scratch buffer, in matrices.
76
+ * 256 × 64 = 16 KB — fits a handful of small skeletons before the first
77
+ * grow. Picked to be wasteful neither at "no skins" nor "one character"
78
+ * scale; growth doubles, so a few realloc cycles cover anything larger.
79
+ */
80
+ const SKIN_MATRICES_INITIAL_CAPACITY = 256;
81
+
82
+ /**
83
+ * Initial capacity of the {@link #pose_accumulator_buffer} in rows
84
+ * (= transforms-table slots). 256 × 64 = 16 KB; growth doubles to
85
+ * fit larger scenes. Resolve always zeroes the slot after draining,
86
+ * so a fresh buffer (createBuffer is zero-initialised) and a "no
87
+ * contribution this frame" state are observationally identical.
88
+ */
89
+ const POSE_ACCUMULATOR_INITIAL_CAPACITY = 256;
90
+
91
+ /**
92
+ * Per-frame `dt` cap pushed into the tick shader. Without it, a tab
93
+ * backgrounded for several seconds (or a paused debugger) would
94
+ * leak that whole interval into the next tick — either rocketing a
95
+ * looping clip through dozens of cycles in one step or, for
96
+ * non-looping clips, clamping them past the meaningful range. The
97
+ * cap trades a few frames of CSS-smooth animation after a stall
98
+ * for a deterministic clip state.
99
+ */
100
+ const MAX_DT_SECONDS = 1 / 15;
101
+
102
+ /**
103
+ * Allocate a singly-linked chain of fixed-capacity nodes into `table`,
104
+ * one node per `items_per_chunk`-sized window of `items`. Builds
105
+ * tail-first so each node's successor id is known at `add()` time —
106
+ * the alternative (forward + back-patch) would mean a second `set()`
107
+ * per node.
108
+ *
109
+ * `build_node(start, count, next_id)` constructs the record to add: it
110
+ * populates the inline array from `items[start..start+count]`, pads
111
+ * tail slots, and assembles any extra payload the table expects
112
+ * (including embedding `next_id` as the node's `next` field).
113
+ *
114
+ * Returns the head id (the last `add()`'s slot id), or `0xFFFFFFFF` if
115
+ * `items` is empty.
116
+ *
117
+ * @template T
118
+ * @template R
119
+ * @param {{ add: (record: R) => number }} table
120
+ * @param {T[]} items
121
+ * @param {number} items_per_chunk
122
+ * @param {(start: number, count: number, next_id: number) => R} build_node
123
+ * @returns {number}
124
+ */
125
+ function add_linked_chain(table, items, items_per_chunk, build_node) {
126
+ const node_count = Math.ceil(items.length / items_per_chunk);
127
+ let next_id = 0xFFFFFFFF;
128
+
129
+ for (let n = node_count - 1; n >= 0; n--) {
130
+ const start = n * items_per_chunk;
131
+ const this_count = Math.min(items_per_chunk, items.length - start);
132
+ next_id = table.add(build_node(start, this_count, next_id));
133
+ }
134
+
135
+ return next_id;
136
+ }
137
+
138
+ /**
139
+ * Top-level coordinator for GPU-resident animation.
140
+ *
141
+ * Owns a single {@link GPUDatabase} keyed by {@link ANIMATION_DATABASE_SPEC}
142
+ * with five tables: `animation_curves`, `animation_keyframe_blocks`,
143
+ * `animation_tracks`, `animation_clips`, `animation_bound_track_groups`.
144
+ * Every record — including individual keyframes (inlined inside block
145
+ * nodes) and individual bindings (inlined inside group nodes) — lives in
146
+ * the database, so the dispatch shader needs only one storage binding.
147
+ *
148
+ * Animation *data* (curves, keyframes, tracks) is independent from
149
+ * *bound clips* (each carrying a `time` and a head pointer to its
150
+ * bound-track group chain). Bindings are no longer their own table:
151
+ * each chain node inlines up to {@link TRACKS_PER_GROUP_NODE} bindings
152
+ * and back-references its owning clip.
153
+ *
154
+ * Usage:
155
+ * 1. (Once at setup) Either low-level (`add_curve`, `add_track`,
156
+ * `add_skin`, `add_clip`) or high-level (`register_skin`,
157
+ * `register_clip` against the CPU data classes {@link Skin} /
158
+ * {@link ShadeAnimationClip}).
159
+ * 2. (Once) {@link start} per clip to begin GPU-driven playback —
160
+ * the {@link shader_animation_tick} pass advances `time` each
161
+ * frame thereafter. CPU-driven prototypes can instead call
162
+ * {@link set_time} per frame and skip the Playing flag.
163
+ * 3. (Per frame) {@link update} flushes pending writes for the
164
+ * database. The scene context already does this on its own
165
+ * update path.
166
+ */
167
+ export class GPUAnimationManager {
168
+
169
+ /** @type {GPUDevice} */
170
+ #device;
171
+
172
+ /** @type {string} */
173
+ #label;
174
+
175
+ /** @type {GPUDatabase} */
176
+ #database;
177
+
178
+ /**
179
+ * Ping-pong pair of per-frame skinning-matrix buffers — one holds
180
+ * this frame's matrices (target of the prep dispatch, source for
181
+ * skinning's current-position write), the other last frame's
182
+ * (source for skinning's `position_prev` write that feeds the
183
+ * velocity post-pass).
184
+ *
185
+ * Ping-pong rather than a single buffer because the prev-frame
186
+ * matrices need to survive next frame's prep dispatch — and the
187
+ * cheapest way to preserve them is to write the new matrices into
188
+ * the OTHER buffer instead of overwriting the only one we have.
189
+ *
190
+ * {@link #skin_matrices_current_index} selects which entry of this
191
+ * array is "current" for the frame about to be dispatched;
192
+ * {@link dispatch_skin_matrix_prep} flips it before each frame's
193
+ * prep so the now-stale slot gets recycled.
194
+ *
195
+ * Lazily allocated on first {@link add_skin}; both grow together
196
+ * by doubling. After a grow both buffers are zero-initialised and
197
+ * {@link #first_frame_skin_matrices} is set so the next prep
198
+ * dispatch seeds the "previous" buffer with a copy of the freshly
199
+ * written "current" — without this, the first velocity readback
200
+ * after a grow would interpolate against zero matrices.
201
+ *
202
+ * @type {Array<GPUBuffer|null>}
203
+ */
204
+ #skin_matrices_buffers = [null, null];
205
+
206
+ /**
207
+ * Which entry of {@link #skin_matrices_buffers} is "current" for
208
+ * the next prep dispatch. Flipped at the start of every prep call.
209
+ * @type {number}
210
+ */
211
+ #skin_matrices_current_index = 0;
212
+
213
+ /**
214
+ * When true, the next prep dispatch will copy the freshly-written
215
+ * "current" buffer into the "previous" buffer at the end of the
216
+ * pass — used on first allocation and after every grow so the
217
+ * very first velocity computation has matching prev/current
218
+ * matrices (zero velocity).
219
+ * @type {boolean}
220
+ */
221
+ #first_frame_skin_matrices = true;
222
+
223
+ /**
224
+ * Capacity of each of {@link #skin_matrices_buffers} in matrices
225
+ * (not bytes). Both buffers always have the same capacity.
226
+ * @type {number}
227
+ */
228
+ #skin_matrices_capacity = 0;
229
+
230
+ /**
231
+ * High-water mark of the matrix range: the first index no skin has ever been given.
232
+ * A skin that cannot be served from {@link #free_skin_matrix_ranges} takes
233
+ * `joint_count` consecutive entries starting here, and this advances past them.
234
+ *
235
+ * Monotonic, but no longer the whole allocator — a range given back is reused before
236
+ * this advances again, so it tracks peak concurrent joints rather than total
237
+ * registrations.
238
+ *
239
+ * @type {number}
240
+ */
241
+ #next_skin_matrix_offset = 0;
242
+
243
+ /**
244
+ * CPU-side `skin_id → skin_matrix_offset` map. Each registered
245
+ * skin's base address in the shared `skin_matrices` scratch
246
+ * buffer; the per-block absolute offset (the prep shader's hot
247
+ * path) is derived as `base + chain_position *
248
+ * JOINTS_PER_SKIN_BLOCK` at registration. Storing the base on
249
+ * GPU would duplicate it — the block records already carry the
250
+ * per-block absolute address, and only the CPU (the skinning-
251
+ * binding upload path) needs the skin's base value.
252
+ *
253
+ * @type {Map<number, number>}
254
+ */
255
+ #skin_matrix_offsets = new Map();
256
+
257
+ /**
258
+ * Matrix ranges handed back by {@link unregister_skin}, bucketed by their length in
259
+ * matrices. Each bucket is a stack of base indices free to give out again.
260
+ *
261
+ * A range's base is baked into its skin's joint-block rows on the GPU, so a range can
262
+ * never be *moved* once handed out — which is what made compaction hard enough to skip,
263
+ * and left the offset a bump pointer with no way back. Reuse needs no movement: the hole
264
+ * a despawned character leaves is exactly the size of the next character wearing that
265
+ * rig, so matching a request to a hole of the same length hands the range straight back
266
+ * with nothing relocated and nothing to patch.
267
+ *
268
+ * Bucketed by length because the match has to be **exact**. The prep pass writes
269
+ * `base + joint` for `joint` in `[0, joint_count)`, so a skin placed in a shorter hole
270
+ * would write over whatever lives after it. Splitting a longer hole would fit, but every
271
+ * split leaves a fragment only an exactly-as-small skin can ever use; a game's rigs come
272
+ * in a handful of joint counts, so exact buckets stay few and nearly always hit.
273
+ *
274
+ * What this does not reclaim is bounded by the number of distinct joint counts ever
275
+ * registered rather than by the number of registrations: a rig retired for good leaves
276
+ * its ranges behind, a rig still in use passes the same ranges around forever.
277
+ *
278
+ * @type {Map<number, number[]>}
279
+ */
280
+ #free_skin_matrix_ranges = new Map();
281
+
282
+ /**
283
+ * Registered skins by id, kept for one reason: a clip registered *after* a skin has to be held
284
+ * against it, and a clip registered before one has to be held against the skin when it arrives.
285
+ * The frame a skinned mesh is deformed in is fixed once, at registration, and a clip that scales
286
+ * a joint is the one thing that can undo it afterwards — see {@link skin_clip_scale_conflicts}
287
+ * and `SKINNING_FRAME_PLAN.md` §4.
288
+ *
289
+ * @type {Map<number, Skin>}
290
+ */
291
+ #skins_by_id = new Map();
292
+
293
+ /**
294
+ * Registered clips that scale *something*, by id. Almost always empty — a character's clips
295
+ * rotate and translate — which is what keeps the pairwise check free in the ordinary case.
296
+ *
297
+ * @type {Map<number, ShadeAnimationClip>}
298
+ */
299
+ #scaling_clips = new Map();
300
+
301
+ /**
302
+ * Per-frame pose-accumulator scratch buffer. One slot per
303
+ * `transforms_table` row (slot size = `POSE_ACCUMULATOR_SLOT_BYTES`).
304
+ * The apply pass atomic-adds weighted TRS contributions into each
305
+ * dirty row; the resolve pass drains it into `transforms_table.local_*`
306
+ * and zeros the slot for next frame.
307
+ *
308
+ * Lazily allocated on first {@link ensure_pose_accumulator_capacity};
309
+ * grows by doubling. Grow destroys the old buffer — since resolve
310
+ * always leaves it zero, no copy is needed on grow.
311
+ *
312
+ * @type {GPUBuffer|null}
313
+ */
314
+ #pose_accumulator_buffer = null;
315
+
316
+ /**
317
+ * Capacity of {@link #pose_accumulator_buffer} in rows (slot
318
+ * indices), not bytes.
319
+ * @type {number}
320
+ */
321
+ #pose_accumulator_capacity = 0;
322
+
323
+ /**
324
+ * Back-reference to the owning scene context. Lets the high-level
325
+ * `register_*` methods resolve `Node3D.id` → transforms-table row
326
+ * via {@link GPUSceneContext#id_mapping} and bind skinning meshes
327
+ * via {@link GPUSceneContext#skinning}, without callers having to
328
+ * thread the context through every call.
329
+ *
330
+ * The manager doesn't touch this at construction (the scene
331
+ * context is itself only partially constructed when it builds the
332
+ * manager) — only inside `register_*` calls, which happen after
333
+ * scene.build has populated `id_mapping`.
334
+ *
335
+ * @type {GPUSceneContext}
336
+ */
337
+ #scene_context;
338
+
339
+ /**
340
+ * Identity-keyed cache of curve-id-on-GPU for every
341
+ * {@link AnimationCurve} that has been registered via
342
+ * {@link register_clip}. Lets multiple clips whose channels share
343
+ * the same curve instance (e.g. one base clip retargeted across
344
+ * many character instances) reuse the same GPU rows for the
345
+ * curve header + keyframe blocks rather than uploading
346
+ * duplicates. At grid-of-dancers scale duplication would balloon
347
+ * the keyframe-blocks table past its page-table limits.
348
+ *
349
+ * Low-level `add_curve` deliberately stays uncached — every call
350
+ * uploads — so existing callers that pre-build curves with
351
+ * distinct IDs (e.g. a pre-built track pool)
352
+ * keep their behavior.
353
+ *
354
+ * @type {WeakMap<AnimationCurve, number>}
355
+ */
356
+ #curve_id_cache = new WeakMap();
357
+
358
+ /**
359
+ * The curves uploaded so far, by what they are rather than which instance they were: the
360
+ * `hash()` of a curve's keys, to every uploaded curve with that hash and its GPU id.
361
+ *
362
+ * An instance cache alone shares nothing between two parses of one file, and a character
363
+ * instanced a few hundred times is a few hundred parses — each with its own instances of the
364
+ * same keys, filling the keyframe-block table with copies of one dance until its page limit
365
+ * refuses the next dancer. Curves are read-only once uploaded and nothing here releases them,
366
+ * so equal content can safely mean one upload.
367
+ *
368
+ * A hash bucket is a list because a hash is not an identity: a curve is only shared with one
369
+ * whose every key `equals` its own, tangents included.
370
+ *
371
+ * @type {Map<number, {curve: AnimationCurve, id: number}[]>}
372
+ */
373
+ #curve_id_by_content = new Map();
374
+
375
+ /**
376
+ * Cache of track-id-on-GPU keyed by `(curve_id_x, curve_id_y,
377
+ * curve_id_z, curve_id_w, mask)`. With curve dedup already
378
+ * sharing curve IDs across clips, two channels in different
379
+ * clips that bind to the same (target_property, curves) end up
380
+ * with identical track keys — and we can share the track row
381
+ * too. Each curve_id slot is `-1` when the corresponding channel
382
+ * is absent.
383
+ *
384
+ * Keys are joined into a string for Map lookup; the join cost is
385
+ * negligible compared to the GPU upload it avoids.
386
+ *
387
+ * @type {Map<string, number>}
388
+ */
389
+ #track_id_cache = new Map();
390
+
391
+ /**
392
+ * Skins registered via {@link register_skin} whose joint-block
393
+ * chain hasn't been built yet because one or more joint Node3Ds
394
+ * lacks an `id_mapping` row. The skin's GPU table row exists
395
+ * (placeholder `joint_block_head = NULL`, no joints driving the
396
+ * prep pass yet), and state mutators / unregister already work
397
+ * — but the deformation only kicks in once {@link update}
398
+ * successfully drains the entry.
399
+ *
400
+ * Each entry: `skin_id`, the CPU {@link Skin} (whose `joints`
401
+ * array drives the chain build), the `skin_matrix_offset`
402
+ * reserved at register time, and the `inverse_bind_matrices`
403
+ * typed array.
404
+ *
405
+ * @type {Array<{
406
+ * skin_id: number,
407
+ * skin: Skin,
408
+ * skin_matrix_offset: number,
409
+ * inverse_bind_matrices: Float32Array,
410
+ * }>}
411
+ */
412
+ #pending_skins = [];
413
+
414
+ /**
415
+ * Clips registered via {@link register_clip} whose bindings
416
+ * chain hasn't been built yet because one or more channel
417
+ * targets lacks an `id_mapping` row. The clip's GPU table row
418
+ * exists (placeholder `bound_track_group_head = NULL`, no
419
+ * tracks driving the apply pass yet); time/state mutators and
420
+ * unregister already work.
421
+ *
422
+ * Each entry: `clip_id` plus the per-channel `(track_id,
423
+ * target, property)` triples — the curves and tracks were
424
+ * interned at register time so only the (target → instance row)
425
+ * resolution is deferred.
426
+ *
427
+ * @type {Array<{
428
+ * clip_id: number,
429
+ * channels: Array<{ track: number, target: Node3D, property: number }>,
430
+ * }>}
431
+ */
432
+ #pending_clips = [];
433
+
434
+ /**
435
+ * @param {GPUDevice} device
436
+ * @param {string} label
437
+ * @param {GPUSceneContext} scene_context
438
+ */
439
+ constructor(device, label, scene_context) {
440
+ assert.defined(device, 'device');
441
+ assert.defined(scene_context, 'scene_context');
442
+
443
+ this.#device = device;
444
+ this.#label = label;
445
+ this.#scene_context = scene_context;
446
+
447
+ this.#database = new GPUDatabase({
448
+ device,
449
+ definition: ANIMATION_DATABASE_SPEC,
450
+ label: `${label}/Database`,
451
+ });
452
+ }
453
+
454
+ /**
455
+ * @returns {GPUDatabase}
456
+ */
457
+ get database() {
458
+ return this.#database;
459
+ }
460
+
461
+ /**
462
+ * Per-frame skinning-matrix scratch buffer for the *current*
463
+ * frame. `null` until the first skin is registered. The
464
+ * skin-matrix-prep pass writes to it; the skinning compute pass
465
+ * reads from it to deform current-frame positions. Address `j`
466
+ * of skin `s` is at matrix index `s.skin_matrix_offset + j`.
467
+ *
468
+ * The buffer reference is invalidated on grow AND flips role
469
+ * every frame as part of the ping-pong with
470
+ * {@link prev_skin_matrices_buffer} — re-fetch each frame.
471
+ *
472
+ * @returns {GPUBuffer|null}
473
+ */
474
+ get skin_matrices_buffer() {
475
+ return this.#skin_matrices_buffers[this.#skin_matrices_current_index];
476
+ }
477
+
478
+ /**
479
+ * Previous-frame counterpart of {@link skin_matrices_buffer} —
480
+ * holds the matrices the previous frame's prep dispatch wrote.
481
+ * Read by the skinning compute pass to produce per-vertex
482
+ * `position_prev` data for the velocity post-pass.
483
+ *
484
+ * `null` until the first skin is registered. Flips role with
485
+ * {@link skin_matrices_buffer} every frame.
486
+ *
487
+ * @returns {GPUBuffer|null}
488
+ */
489
+ get prev_skin_matrices_buffer() {
490
+ return this.#skin_matrices_buffers[1 - this.#skin_matrices_current_index];
491
+ }
492
+
493
+ /**
494
+ * Matrices the buffer has to address: the high-water mark, which covers every registered
495
+ * skin plus any range sitting on the free list between the ones still in use. Independent
496
+ * of buffer capacity, which is at least this big.
497
+ *
498
+ * Not a count of live joints — a skin that unregisters leaves its range addressable for
499
+ * the next skin of its size, so this falls only when nothing has been registered yet.
500
+ *
501
+ * @returns {number}
502
+ */
503
+ get skin_matrix_count() {
504
+ return this.#next_skin_matrix_offset;
505
+ }
506
+
507
+ /**
508
+ * Pose-accumulator scratch buffer (per-Node3D-row weighted
509
+ * contribution slots). `null` until {@link ensure_pose_accumulator_capacity}
510
+ * is called for the first time.
511
+ *
512
+ * The buffer reference is invalidated on grow — re-fetch each
513
+ * frame after {@link ensure_pose_accumulator_capacity} rather
514
+ * than caching across frames.
515
+ *
516
+ * @returns {GPUBuffer|null}
517
+ */
518
+ get pose_accumulator_buffer() {
519
+ return this.#pose_accumulator_buffer;
520
+ }
521
+
522
+ /**
523
+ * Ensure the pose-accumulator scratch buffer can address at least
524
+ * `row_count` `transforms_table` rows. Grows by doubling from the
525
+ * initial capacity; idempotent when already big enough. Called
526
+ * by the per-frame dispatch driver after `scene.build` has
527
+ * stabilised the transforms-table size for the frame.
528
+ *
529
+ * Grow destroys the previous GPU buffer; resolve always leaves the
530
+ * accumulator zeroed at the end of each frame, so there's nothing
531
+ * to copy across the realloc.
532
+ *
533
+ * @param {number} row_count
534
+ */
535
+ ensure_pose_accumulator_capacity(row_count) {
536
+ assert.isNonNegativeInteger(row_count, 'row_count');
537
+
538
+ // Existing buffer fits the request → nothing to do. The null
539
+ // check guarantees the very first call always allocates,
540
+ // even when `row_count == 0` (e.g. before any scene content
541
+ // has been built): downstream binding paths assume the
542
+ // buffer reference is non-null whenever the apply pass runs.
543
+ if (this.#pose_accumulator_buffer !== null && row_count <= this.#pose_accumulator_capacity) {
544
+ return;
545
+ }
546
+
547
+ let new_capacity = Math.max(this.#pose_accumulator_capacity, POSE_ACCUMULATOR_INITIAL_CAPACITY);
548
+ while (new_capacity < row_count) {
549
+ new_capacity *= 2;
550
+ }
551
+
552
+ if (this.#pose_accumulator_buffer !== null) {
553
+ this.#pose_accumulator_buffer.destroy();
554
+ }
555
+
556
+ this.#pose_accumulator_buffer = this.#device.createBuffer({
557
+ label: `${this.#label}/PoseAccumulator`,
558
+ size: new_capacity * POSE_ACCUMULATOR_SLOT_BYTES,
559
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
560
+ });
561
+ this.#pose_accumulator_capacity = new_capacity;
562
+ }
563
+
564
+ /**
565
+ * Read back a skin's record from the database's CPU shadow.
566
+ *
567
+ * @param {number} skin_id
568
+ * @returns {{joint_block_head: number, joint_count: number}}
569
+ */
570
+ get_skin(skin_id) {
571
+ assert.isNonNegativeInteger(skin_id, 'skin_id');
572
+ return this.#database.get('animation_skins').get(skin_id);
573
+ }
574
+
575
+ /**
576
+ * Base address of the skin's matrix range in the shared
577
+ * `skin_matrices` scratch buffer — matrix `j` of the skin lives
578
+ * at `skin_matrices[get_skin_matrix_offset(skin_id) + j]`.
579
+ *
580
+ * Tracked CPU-side rather than on the skin record because the
581
+ * value is only ever consumed by the skinning-binding upload
582
+ * path; the prep shader uses the per-block absolute offset
583
+ * (cheaper than a per-thread skin lookup for that hot path).
584
+ *
585
+ * @param {number} skin_id
586
+ * @returns {number}
587
+ */
588
+ get_skin_matrix_offset(skin_id) {
589
+ assert.isNonNegativeInteger(skin_id, 'skin_id');
590
+ const offset = this.#skin_matrix_offsets.get(skin_id);
591
+ assert.defined(offset, `unknown skin_id ${skin_id}`);
592
+ return offset;
593
+ }
594
+
595
+ /**
596
+ * Add an animation curve. Keyframes are split into linked-list blocks
597
+ * of up to {@link KEYFRAMES_PER_BLOCK} entries each; the curve header
598
+ * points at the head block, and each block's `next` field chains
599
+ * forward.
600
+ *
601
+ * The curve is a cubic Hermite spline — step, linear, and arbitrary
602
+ * cubic shapes are all expressible by choosing tangents on each keyframe.
603
+ *
604
+ * @param {AnimationCurve} curve
605
+ * @returns {number} curve id (index into the `animation_curves` table)
606
+ */
607
+ add_curve(curve) {
608
+ assert.defined(curve, 'curve');
609
+ assert.equal(curve.isAnimationCurve, true, 'curve.isAnimationCurve !== true');
610
+ assert.greaterThanOrEqual(curve.length, 1, 'curve must have at least one keyframe');
611
+
612
+ const keys = curve.keys;
613
+ const blocks_table = this.#database.get('animation_keyframe_blocks');
614
+
615
+ const head_block_id = add_linked_chain(
616
+ blocks_table,
617
+ keys,
618
+ KEYFRAMES_PER_BLOCK,
619
+ (start, count, next) => {
620
+ const keyframes = new Array(KEYFRAMES_PER_BLOCK);
621
+ for (let j = 0; j < KEYFRAMES_PER_BLOCK; j++) {
622
+ keyframes[j] = j < count ? keys[start + j] : ZERO_KEYFRAME;
623
+ }
624
+ return { keyframes, count, next };
625
+ }
626
+ );
627
+
628
+ const curves_table = this.#database.get('animation_curves');
629
+ return curves_table.add({
630
+ kind: 0,
631
+ keyframe_block_head: head_block_id,
632
+ keyframe_count: keys.length,
633
+ time_min: curve.start_time,
634
+ time_max: curve.end_time,
635
+ });
636
+ }
637
+
638
+ /**
639
+ * Add a track. Channels are addressed by name (`x`, `y`, `z`, `w`) — the
640
+ * provided curves go into the matching slots of the track's `vec4f`
641
+ * output. Omitted channels are masked off and read as 0.
642
+ *
643
+ * The slot meaning is property-specific: for `MeshTranslation`, x/y/z
644
+ * are the translation components; for `MeshRotation` they're the
645
+ * quaternion's x/y/z/w. The track's mask travels with the evaluated
646
+ * value to the apply step, which uses it to decide which destinations
647
+ * to overwrite.
648
+ *
649
+ * @param {{ x?: number, y?: number, z?: number, w?: number }} channels
650
+ * curve ids per channel; pass at least one
651
+ * @returns {number} track id (index into the `animation_tracks` table)
652
+ */
653
+ add_track(channels) {
654
+ assert.defined(channels, 'channels');
655
+
656
+ const cx = channels.x;
657
+ const cy = channels.y;
658
+ const cz = channels.z;
659
+ const cw = channels.w;
660
+
661
+ let mask = 0;
662
+ if (cx !== undefined) mask |= 0x1;
663
+ if (cy !== undefined) mask |= 0x2;
664
+ if (cz !== undefined) mask |= 0x4;
665
+ if (cw !== undefined) mask |= 0x8;
666
+
667
+ assert.notEqual(mask, 0, 'track must have at least one channel');
668
+
669
+ const curves = [
670
+ cx ?? 0,
671
+ cy ?? 0,
672
+ cz ?? 0,
673
+ cw ?? 0,
674
+ ];
675
+
676
+ const table = this.#database.get('animation_tracks');
677
+ return table.add({ mask, curves });
678
+ }
679
+
680
+ /**
681
+ * Create a bound clip — a group of bound tracks that share a single
682
+ * `time`. Bindings are split into linked-list group nodes of up to
683
+ * {@link TRACKS_PER_GROUP_NODE} each; the clip points at the head
684
+ * node and each node back-references the clip so the apply shader
685
+ * (one thread per group node) can fetch the clip without walking the
686
+ * chain from the clip end.
687
+ *
688
+ * Tracks may be authored on a clock that doesn't align with
689
+ * `[time_start, time_end]` — their curves are clamped to their own
690
+ * `[time_min, time_max]` during evaluation.
691
+ *
692
+ * Defaults: `time = 0`, `time_start = 0`, `time_end = 0`,
693
+ * `playback_rate = 1`, `playback_weight = 1`, `flags = 0` (not
694
+ * playing, not looping). The clip is inert at creation — call
695
+ * {@link start} to begin GPU-side time advancement.
696
+ *
697
+ * @param {{
698
+ * time?: number,
699
+ * time_start?: number,
700
+ * time_end?: number,
701
+ * bindings: Array<{ track: number, instance: number, property: number }>,
702
+ * }} clip
703
+ * @returns {number} clip id (index into the `animation_clips` table)
704
+ */
705
+ add_clip(clip) {
706
+ assert.defined(clip, 'clip');
707
+ assert.isArray(clip.bindings, 'clip.bindings');
708
+ assert.greaterThanOrEqual(clip.bindings.length, 1, 'clip must have at least one binding');
709
+
710
+ const time = clip.time ?? 0;
711
+ const time_start = clip.time_start ?? 0;
712
+ const time_end = clip.time_end ?? 0;
713
+ const bindings = clip.bindings;
714
+
715
+ const clips_table = this.#database.get('animation_clips');
716
+ const groups_table = this.#database.get('animation_bound_track_groups');
717
+
718
+ // Reserve the clip slot up front so group nodes can back-reference
719
+ // it via `clip`. The head pointer is patched in once the chain is
720
+ // built (we don't know its first id until we've allocated every
721
+ // group node).
722
+ const clip_id = clips_table.add({
723
+ time,
724
+ time_start,
725
+ time_end,
726
+ bound_track_group_head: ANIMATION_BOUND_TRACK_GROUP_NULL,
727
+ playback_rate: 1,
728
+ flags: 0,
729
+ playback_weight: 1,
730
+ });
731
+
732
+ const head_group_id = add_linked_chain(
733
+ groups_table,
734
+ bindings,
735
+ TRACKS_PER_GROUP_NODE,
736
+ (start, count, next) => {
737
+ const inline_bindings = new Array(TRACKS_PER_GROUP_NODE);
738
+ for (let j = 0; j < TRACKS_PER_GROUP_NODE; j++) {
739
+ if (j < count) {
740
+ const b = bindings[start + j];
741
+
742
+ assert.isNonNegativeInteger(b.track, `clip.bindings[${start + j}].track`);
743
+ assert.isNonNegativeInteger(b.instance, `clip.bindings[${start + j}].instance`);
744
+ assert.isNonNegativeInteger(b.property, `clip.bindings[${start + j}].property`);
745
+
746
+ inline_bindings[j] = b;
747
+ } else {
748
+ inline_bindings[j] = ZERO_BINDING;
749
+ }
750
+ }
751
+ return { bindings: inline_bindings, count, next, clip: clip_id };
752
+ }
753
+ );
754
+
755
+ // Patch the head pointer now that the chain exists. Re-`set` the
756
+ // whole record because the table API doesn't expose per-field
757
+ // updates.
758
+ clips_table.set(clip_id, {
759
+ time,
760
+ time_start,
761
+ time_end,
762
+ bound_track_group_head: head_group_id,
763
+ playback_rate: 1,
764
+ flags: 0,
765
+ playback_weight: 1,
766
+ });
767
+
768
+ return clip_id;
769
+ }
770
+
771
+ /**
772
+ * Overwrite a single field of a clip's record by reading the current
773
+ * record back from the CPU shadow and re-`set`ting with one field
774
+ * changed. The table API doesn't expose per-field updates, so we
775
+ * round-trip through a full record; the upload-buffer dedupe
776
+ * collapses the resulting per-frame write into a single GPU touch
777
+ * when nothing else changed.
778
+ *
779
+ * @param {number} clip_id
780
+ * @param {string} field
781
+ * @param {number} value
782
+ */
783
+ #patch_clip_field(clip_id, field, value) {
784
+ assert.isNonNegativeInteger(clip_id, 'clip_id');
785
+
786
+ const clips_table = this.#database.get('animation_clips');
787
+ const current = clips_table.get(clip_id);
788
+
789
+ clips_table.set(clip_id, {
790
+ time: current.time,
791
+ time_start: current.time_start,
792
+ time_end: current.time_end,
793
+ bound_track_group_head: current.bound_track_group_head,
794
+ playback_rate: current.playback_rate,
795
+ flags: current.flags,
796
+ playback_weight: current.playback_weight,
797
+ [field]: value,
798
+ });
799
+ }
800
+
801
+ /**
802
+ * Direct override of a clip's `time`. Bypasses {@link start} /
803
+ * {@link stop} — `time` is written even if the clip is paused, and
804
+ * the {@link AnimationClipFlags.Playing} bit is left untouched.
805
+ * The host typically uses this to scrub or rewind; GPU-driven
806
+ * playback advances time on its own via the tick shader.
807
+ *
808
+ * TODO: race between `start()` and `set_time()` is non-atomic — if
809
+ * a caller does `start(id); set_time(id, phase)` and the next GPU
810
+ * tick fires before the page upload from `set_time` lands, the
811
+ * tick advances from the old `time` for one frame. RIS would be
812
+ * an atomic `start_at(id, time, ...)` that fuses both field
813
+ * writes into one record set. For phase offsets the one-frame
814
+ * skew is invisible; for scrub-and-resume UIs it can show.
815
+ *
816
+ * @param {number} clip_id
817
+ * @param {number} time
818
+ */
819
+ set_time(clip_id, time) {
820
+ assert.isNumber(time, 'time');
821
+ this.#patch_clip_field(clip_id, 'time', time);
822
+ }
823
+
824
+ /**
825
+ * Direct override of a clip's per-frame advance scalar. `1.0` =
826
+ * realtime; `0.0` effectively pauses (independent of the
827
+ * {@link AnimationClipFlags.Playing} bit); negative values play
828
+ * in reverse.
829
+ *
830
+ * @param {number} clip_id
831
+ * @param {number} rate
832
+ */
833
+ set_playback_rate(clip_id, rate) {
834
+ assert.isNumber(rate, 'rate');
835
+ this.#patch_clip_field(clip_id, 'playback_rate', rate);
836
+ }
837
+
838
+ /**
839
+ * Override a clip's contribution weight into the pose accumulator.
840
+ * `1.0` is full strength; `0.0` silences the clip without removing
841
+ * its bindings; values in between blend it against other clips
842
+ * targeting the same nodes. The apply shader pre-multiplies every
843
+ * curve-evaluated value by this scalar before atomic-adding into
844
+ * the accumulator.
845
+ *
846
+ * Normalized blends expect weights across all clips driving a
847
+ * node to sum to `1`; additive layers use weights that don't sum
848
+ * to `1`. The system doesn't enforce either — host's call.
849
+ *
850
+ * @param {number} clip_id
851
+ * @param {number} weight
852
+ */
853
+ set_playback_weight(clip_id, weight) {
854
+ assert.isNumber(weight, 'weight');
855
+ this.#patch_clip_field(clip_id, 'playback_weight', weight);
856
+ }
857
+
858
+ /**
859
+ * Add (OR) one or more flag bits onto a clip's bitfield. Bits
860
+ * already set stay set; other bits are untouched. Values come
861
+ * from {@link AnimationClipFlags} and can be combined with `|`:
862
+ * `set_flags(id, AnimationClipFlags.Loop | AnimationClipFlags.Playing)`.
863
+ *
864
+ * Counterpart {@link clear_flags} ANDs the bits out. Together
865
+ * they cover the common cases without the overwrite footgun
866
+ * the original version had (where setting Loop would silently
867
+ * clear Playing).
868
+ *
869
+ * For toggling just the playing bit, {@link start} / {@link stop}
870
+ * are equivalent shortcuts.
871
+ *
872
+ * @param {number} clip_id
873
+ * @param {number} flags bits to set
874
+ */
875
+ set_flags(clip_id, flags) {
876
+ assert.isNonNegativeInteger(flags, 'flags');
877
+
878
+ const clips_table = this.#database.get('animation_clips');
879
+ const current = clips_table.get(clip_id);
880
+
881
+ this.#patch_clip_field(clip_id, 'flags', current.flags | flags);
882
+ }
883
+
884
+ /**
885
+ * Clear (AND-NOT) one or more flag bits from a clip's bitfield.
886
+ * Bits not present in `flags` are left untouched. Symmetric
887
+ * with {@link set_flags}.
888
+ *
889
+ * @param {number} clip_id
890
+ * @param {number} flags bits to clear
891
+ */
892
+ clear_flags(clip_id, flags) {
893
+ assert.isNonNegativeInteger(flags, 'flags');
894
+
895
+ const clips_table = this.#database.get('animation_clips');
896
+ const current = clips_table.get(clip_id);
897
+
898
+ this.#patch_clip_field(clip_id, 'flags', current.flags & ~flags);
899
+ }
900
+
901
+ /**
902
+ * Begin GPU-side playback. Sets the {@link AnimationClipFlags.Playing}
903
+ * bit; preserves every other flag bit, the current `time`, the
904
+ * `playback_rate`, and so on. Calling {@link stop} and then `start`
905
+ * back-to-back should feel like a no-op modulo at most one tick's
906
+ * worth of advance.
907
+ *
908
+ * Idempotent: starting an already-playing clip is a no-op (the
909
+ * upload-buffer dedupe collapses the write).
910
+ *
911
+ * @param {number} clip_id
912
+ */
913
+ start(clip_id) {
914
+ assert.isNonNegativeInteger(clip_id, 'clip_id');
915
+
916
+ const clips_table = this.#database.get('animation_clips');
917
+ const current = clips_table.get(clip_id);
918
+
919
+ this.#patch_clip_field(clip_id, 'flags', current.flags | AnimationClipFlags.Playing);
920
+ }
921
+
922
+ /**
923
+ * Pause GPU-side playback. Clears the {@link AnimationClipFlags.Playing}
924
+ * bit; preserves every other flag bit and the current `time`.
925
+ * Symmetric with {@link start} — a stop/start pair leaves the clip
926
+ * where it was (give or take one tick of GPU time loss).
927
+ *
928
+ * Idempotent: stopping an already-stopped clip is a no-op.
929
+ *
930
+ * @param {number} clip_id
931
+ */
932
+ stop(clip_id) {
933
+ assert.isNonNegativeInteger(clip_id, 'clip_id');
934
+
935
+ const clips_table = this.#database.get('animation_clips');
936
+ const current = clips_table.get(clip_id);
937
+
938
+ this.#patch_clip_field(clip_id, 'flags', current.flags & ~AnimationClipFlags.Playing);
939
+ }
940
+
941
+ /**
942
+ * Register a skin: an ordered list of joints, each pinning a
943
+ * Node3D row that drives the joint plus the inverse-bind matrix
944
+ * that takes vertex positions from mesh space into the joint's
945
+ * bind-pose local space. The skin reserves a contiguous range of
946
+ * `joints.length` matrices in the {@link #skin_matrices_buffer}
947
+ * scratch buffer; the prep pass populates that range each frame
948
+ * with `node.global × inverse_bind` for every joint.
949
+ *
950
+ * The skin record carries no mesh-world factor. The rasterizer
951
+ * applies `mesh.transform_global` to the skinned clone vertex
952
+ * via the standard mesh transform path
953
+ * ({@link chunk_mesh_resolve_vertex_position_world}), so adding
954
+ * an `inverse(mesh.global)` factor in the skin matrix would
955
+ * double-cancel against that re-application and shrink the rig
956
+ * back to mesh-local coordinates rendered as world.
957
+ *
958
+ * Joints are split into linked-list blocks of up to
959
+ * {@link JOINTS_PER_SKIN_BLOCK} each — same chain pattern used for
960
+ * bound-track groups and keyframe blocks. Each block records the
961
+ * absolute write offset for its slots, so the prep shader's
962
+ * per-thread work is one block load + a bounded inner loop with no
963
+ * indirection back through the skin record.
964
+ *
965
+ * @param {{
966
+ * joints: Array<{ node: number, inverse_bind: Float32Array | number[] }>,
967
+ * }} skin
968
+ * @returns {number} skin id (index into the `animation_skins` table)
969
+ */
970
+ add_skin(skin) {
971
+ assert.defined(skin, 'skin');
972
+ assert.isArray(skin.joints, 'skin.joints');
973
+ assert.greaterThanOrEqual(skin.joints.length, 1, 'skin must have at least one joint');
974
+
975
+ const joints = skin.joints;
976
+ const joint_count = joints.length;
977
+
978
+ // Reserve the matrix range. Each block bakes in its absolute
979
+ // slot offset (skin_base + chain_position * JOINTS_PER_SKIN_BLOCK)
980
+ // so the prep shader writes directly without dereferencing
981
+ // the skin row for the offset; the base is also stashed on
982
+ // the CPU map so the skinning-binding upload can look it up
983
+ // without reading the GPU back.
984
+ const skin_matrix_offset = this.#allocate_skin_matrix_range(joint_count);
985
+
986
+ const skins_table = this.#database.get('animation_skins');
987
+ const blocks_table = this.#database.get('animation_skin_joint_blocks');
988
+
989
+ // Reserve the skin row up front. The head pointer is patched
990
+ // in once the chain exists. NULL placeholder — 0 is a valid
991
+ // block row id (the first allocation in an empty table).
992
+ const skin_id = skins_table.add({
993
+ joint_block_head: ANIMATION_SKIN_JOINT_BLOCK_NULL,
994
+ joint_count,
995
+ });
996
+ this.#skin_matrix_offsets.set(skin_id, skin_matrix_offset);
997
+
998
+ const head_block_id = add_linked_chain(
999
+ blocks_table,
1000
+ joints,
1001
+ JOINTS_PER_SKIN_BLOCK,
1002
+ (start, count, next) => {
1003
+ const inline_joints = new Array(JOINTS_PER_SKIN_BLOCK);
1004
+ for (let j = 0; j < JOINTS_PER_SKIN_BLOCK; j++) {
1005
+ if (j < count) {
1006
+ const joint = joints[start + j];
1007
+
1008
+ assert.isNonNegativeInteger(joint.node, `skin.joints[${start + j}].node`);
1009
+ assert.defined(joint.inverse_bind, `skin.joints[${start + j}].inverse_bind`);
1010
+
1011
+ inline_joints[j] = joint;
1012
+ } else {
1013
+ inline_joints[j] = ZERO_SKIN_JOINT;
1014
+ }
1015
+ }
1016
+ return {
1017
+ joints: inline_joints,
1018
+ count,
1019
+ next,
1020
+ skin_matrix_offset: skin_matrix_offset + start,
1021
+ };
1022
+ }
1023
+ );
1024
+
1025
+ // Patch the head pointer now that the chain exists. Re-`set`
1026
+ // the whole record because the table API doesn't expose
1027
+ // per-field updates.
1028
+ skins_table.set(skin_id, {
1029
+ joint_block_head: head_block_id,
1030
+ joint_count,
1031
+ });
1032
+
1033
+ return skin_id;
1034
+ }
1035
+
1036
+ /**
1037
+ * High-level skin registration: consumes a {@link Skin} (CPU data
1038
+ * with Node3D joint references and a flat inverse-bind matrix
1039
+ * array), allocates a GPU skin record, and binds every entry in
1040
+ * `skin.meshes` through the skinning context.
1041
+ *
1042
+ * **Eventual consistency.** The `skin_id` is allocated and
1043
+ * returned synchronously — callers can immediately pass it to
1044
+ * state mutators or {@link unregister_skin}. If any joint's
1045
+ * Node3D hasn't been added to a scene yet (no `id_mapping` row),
1046
+ * the joint-block chain that drives the deformation is queued
1047
+ * and built on the next {@link update} that sees all joints
1048
+ * resolved. The skin's table row uses a placeholder
1049
+ * `joint_block_head` until then — observationally indistinguishable
1050
+ * from "skin with no joints driving it" (no deformation runs).
1051
+ *
1052
+ * Mesh→skin bindings register immediately via
1053
+ * {@link GPUMeshSkinningContext#bind}; the skinning context
1054
+ * already tolerates pre-scene-build registration. Each mesh also
1055
+ * gets its {@link SkinnedMesh#skin} reference and a fresh set of
1056
+ * bounds off it, synchronously — that part does not wait for
1057
+ * `id_mapping`, because it reads the joints' `transform_global`
1058
+ * rather than any GPU row.
1059
+ *
1060
+ * @param {Skin} skin
1061
+ * @returns {number} skin id (index into the `animation_skins` table)
1062
+ */
1063
+ register_skin(skin) {
1064
+ assert.defined(skin, 'skin');
1065
+ assert.equal(skin.isSkin, true, 'skin.isSkin !== true');
1066
+ assert.greaterThanOrEqual(skin.meshes.length, 1, 'skin.meshes must contain at least one mesh');
1067
+
1068
+ const joint_count = skin.joints.length;
1069
+ const inverse_bind_matrices = skin.inverse_bind_matrices;
1070
+
1071
+ // Reserve the matrix range up front — block records carry
1072
+ // their absolute slot offset, so the prep shader writes
1073
+ // directly. The base also lands on #skin_matrix_offsets so
1074
+ // skinning-binding upload can look it up.
1075
+ const skin_matrix_offset = this.#allocate_skin_matrix_range(joint_count);
1076
+
1077
+ // Reserve the skin row immediately with a placeholder head
1078
+ // pointer. State mutators and the public id are usable from
1079
+ // this point on; the joint chain gets patched in once
1080
+ // id_mapping has every joint (possibly synchronously below,
1081
+ // possibly deferred via the pending queue).
1082
+ const skins_table = this.#database.get('animation_skins');
1083
+ const skin_id = skins_table.add({
1084
+ // NULL placeholder — 0 is a valid block row id
1085
+ joint_block_head: ANIMATION_SKIN_JOINT_BLOCK_NULL,
1086
+ joint_count,
1087
+ });
1088
+ this.#skin_matrix_offsets.set(skin_id, skin_matrix_offset);
1089
+
1090
+ // Bind every mesh into the skinning context. Idempotent and
1091
+ // pre-build tolerant — the binding upload defers until
1092
+ // id_mapping is ready.
1093
+ //
1094
+ // Each mesh also takes a reference to the CPU-side skin and
1095
+ // re-derives its bounds off it. From here on the skin's
1096
+ // formula is what places the mesh's vertices, and `Mesh`'s
1097
+ // composition — rest-pose geometry under `transform_global` —
1098
+ // is the wrong one, because the skinning pass cancels that
1099
+ // transform back out (see {@link SkinnedMesh#updateBoundsBasic}).
1100
+ // Nothing else would notice: bounds are refreshed by
1101
+ // `updateMatrices`, and a character does not move again just
1102
+ // because it finished loading.
1103
+ // Before any of that: put the meshes into the frame this skin actually deforms in. The
1104
+ // skinning pass writes each clone's vertices in `inverse(mesh.global)` space, so
1105
+ // `mesh.global` is that frame — and glTF says a skinned mesh's node transform is ignored,
1106
+ // which makes the frame the engine's to choose rather than the exporter's to get right.
1107
+ // Ahead of the bind loop because it moves the mesh, and the bounds published below have to
1108
+ // be the ones that survive. See `SKINNING_FRAME_PLAN.md`.
1109
+ this.#normalize_skin_frame(skin);
1110
+
1111
+ this.#skins_by_id.set(skin_id, skin);
1112
+
1113
+ for (const clip of this.#scaling_clips.values()) {
1114
+ this.#report_scale_conflicts(skin, clip);
1115
+ }
1116
+
1117
+ const skinning = this.#scene_context.skinning;
1118
+ const meshes = skin.meshes;
1119
+ for (let i = 0; i < meshes.length; i++) {
1120
+ const mesh = meshes[i];
1121
+
1122
+ skinning.bind(mesh, skin_id);
1123
+
1124
+ mesh.skin = skin;
1125
+ mesh.updateBoundsBasic();
1126
+
1127
+ // The row the scene context published for this mesh holds the pre-skin bounds, and
1128
+ // nothing about binding a skin looks like movement — `updateBoundsBasic` is not
1129
+ // `updateMatrices` and does not touch the version the sync compares against. Without
1130
+ // this the corrected box sits on the CPU object and never reaches the table.
1131
+ mesh.needsUpdate = true;
1132
+ }
1133
+
1134
+ // Try to complete now. If id_mapping is ready for every
1135
+ // joint, the chain gets built and patched in. Otherwise the
1136
+ // skin lands on #pending_skins and the next update() drain
1137
+ // retries.
1138
+ const pending = {
1139
+ skin_id,
1140
+ skin,
1141
+ skin_matrix_offset,
1142
+ inverse_bind_matrices,
1143
+ };
1144
+ if (!this.#try_complete_skin(pending)) {
1145
+ this.#pending_skins.push(pending);
1146
+ }
1147
+
1148
+ return skin_id;
1149
+ }
1150
+
1151
+ /**
1152
+ * Put a skin's meshes into the frame it deforms in, and say so when they cannot be.
1153
+ *
1154
+ * The correction itself is {@link skin_normalize_mesh_frame}, which is where the argument lives.
1155
+ * This is the reporting: an asset that renders wrong should say which asset and by how much, on
1156
+ * the frame it is registered, rather than reaching a person as a screenshot of soup.
1157
+ *
1158
+ * @param {Skin} skin
1159
+ */
1160
+ #normalize_skin_frame(skin) {
1161
+ const report = skin_normalize_mesh_frame(skin);
1162
+
1163
+ // a glTF skin's `name` is optional and often absent; the mesh it deforms is what a person
1164
+ // would recognise, and is what the asset's own hierarchy calls it
1165
+ const label = skin.name !== '' ? skin.name : (skin.meshes[0]?.name ?? '<unnamed>');
1166
+
1167
+ if (report.unwalked.length > 0) {
1168
+ console.error(
1169
+ `skin '${label}': ${report.unwalked.length} of its nodes have never been walked — `
1170
+ + `'${report.unwalked[0].name}' among them — so their world transforms are the `
1171
+ + `identity by default rather than by derivation. Call updateMatrices on the scene `
1172
+ + `before registering. Nothing was moved; the deformation will be wrong.`
1173
+ );
1174
+
1175
+ return;
1176
+ }
1177
+
1178
+ if (report.frame_defect > 0.001) {
1179
+ console.error(
1180
+ `skin '${label}': no frame can deform it — `
1181
+ + `inverse(frame) × joint.global × inverse_bind is ${report.frame_defect.toFixed(4)} `
1182
+ + `away from rigid at its own bind pose, and a dual quaternion carries a rotation and `
1183
+ + `a translation and nothing else. The rest pose has a non-uniform scale or a shear `
1184
+ + `in it. Rendering will be wrong; see SKINNING_FRAME_PLAN.md §4.`
1185
+ );
1186
+
1187
+ return;
1188
+ }
1189
+
1190
+ if (report.residual > 0.001) {
1191
+ console.error(
1192
+ `skin '${label}': its mesh frame did not land — ${report.residual.toFixed(6)} `
1193
+ + `left over. A correction carrying shear does not survive a Transform64's TRS `
1194
+ + `components. Rendering will be wrong; see SKINNING_FRAME_PLAN.md §3.`
1195
+ );
1196
+
1197
+ return;
1198
+ }
1199
+
1200
+ for (const mesh of report.obstructed) {
1201
+ console.warn(
1202
+ `skin '${label}': mesh node '${mesh.name}' carries the skin's frame now, and its `
1203
+ + `${mesh.children.length} child node(s) inherit it. Sockets belong on bones.`
1204
+ );
1205
+ }
1206
+
1207
+ if (report.corrected > 0) {
1208
+ // verbose rather than a warning: this is a legal asset shape, and a crowd of them would
1209
+ // otherwise be a crowd of log lines
1210
+ console.debug(
1211
+ `skin '${label}': ${report.corrected} mesh frame(s) moved onto the skin's bind `
1212
+ + `transform, absorbing ${report.absorbed.toFixed(4)} of non-rigidity the skinning `
1213
+ + `pass could not have carried.`
1214
+ );
1215
+ }
1216
+ }
1217
+
1218
+ /**
1219
+ * Say so when a clip scales a skin's joints out from under the frame it is deformed in.
1220
+ *
1221
+ * The frame {@link skin_normalize_mesh_frame} chooses is fixed at registration, and it is sound
1222
+ * because the joints and the mesh share their scaling ancestors. A scale that reaches only some
1223
+ * of them breaks that every frame it plays, on the GPU, where nothing can see it —
1224
+ * {@link skin_clip_scale_conflicts} is that question asked off the clip's own curves instead.
1225
+ *
1226
+ * Nothing is refused: the clip is legal animation and the skin is a legal skin, and it is their
1227
+ * combination the skinning pass cannot carry. Refusing either would be picking one to blame.
1228
+ *
1229
+ * @param {Skin} skin
1230
+ * @param {ShadeAnimationClip} clip
1231
+ */
1232
+ #report_scale_conflicts(skin, clip) {
1233
+ const conflicts = skin_clip_scale_conflicts(skin, clip);
1234
+
1235
+ if (conflicts.length === 0) {
1236
+ return;
1237
+ }
1238
+
1239
+ const skin_label = skin.name !== '' ? skin.name : (skin.meshes[0]?.name ?? '<unnamed>');
1240
+
1241
+ const nodes = conflicts.map(conflict => conflict.node.name || '<unnamed>').join(', ');
1242
+
1243
+ const uniform = conflicts.every(conflict => conflict.uniform);
1244
+
1245
+ console.error(
1246
+ `clip '${clip.name}' scales ${conflicts.length} node(s) of skin '${skin_label}' — `
1247
+ + `${nodes} — which reach its joints but not its mesh, so the scale enters the joint `
1248
+ + `matrices and not the frame they are folded against. A dual quaternion cannot carry `
1249
+ + `it and no choice of frame fixes it: the deformation will be wrong while this clip `
1250
+ + `plays. `
1251
+ + (uniform
1252
+ ? `The scale is uniform, which SKINNING_FRAME_PLAN.md §4 has a design for.`
1253
+ : `The scale is non-uniform, which no dual quaternion holds — §4's linear-blend `
1254
+ + `path or an import-time refusal.`)
1255
+ );
1256
+ }
1257
+
1258
+ /**
1259
+ * Attempt to build the joint-block chain for a pending skin and
1260
+ * patch its head pointer onto the skin row. Returns `true` when
1261
+ * every joint Node3D has resolved to a transforms-table row
1262
+ * (and the chain was built); `false` when at least one joint is
1263
+ * still missing.
1264
+ *
1265
+ * @param {{
1266
+ * skin_id: number,
1267
+ * skin: Skin,
1268
+ * skin_matrix_offset: number,
1269
+ * inverse_bind_matrices: Float32Array,
1270
+ * }} pending
1271
+ * @returns {boolean}
1272
+ */
1273
+ #try_complete_skin(pending) {
1274
+ const id_mapping = this.#scene_context.id_mapping;
1275
+ const joint_nodes = pending.skin.joints;
1276
+ const joint_count = joint_nodes.length;
1277
+ const inverse_bind_matrices = pending.inverse_bind_matrices;
1278
+ const skin_matrix_offset = pending.skin_matrix_offset;
1279
+ const skin_id = pending.skin_id;
1280
+
1281
+ // All-or-nothing: a single missing row leaves the chain
1282
+ // unbuilt — partial chains aren't valid (the prep shader
1283
+ // walks every block).
1284
+ const joints = new Array(joint_count);
1285
+ for (let i = 0; i < joint_count; i++) {
1286
+ const node = joint_nodes[i];
1287
+ const row = id_mapping.get(node.id);
1288
+ if (row === undefined) return false;
1289
+
1290
+ joints[i] = {
1291
+ node: row,
1292
+ // Per-joint slice of the flat matrix array. The
1293
+ // marshaller copies these bytes into the skin-joint
1294
+ // block record; sub-array views are fine — they
1295
+ // share the underlying buffer but write_struct_value
1296
+ // reads by length, not by offset.
1297
+ inverse_bind: inverse_bind_matrices.subarray(i * 16, (i + 1) * 16),
1298
+ };
1299
+ }
1300
+
1301
+ const blocks_table = this.#database.get('animation_skin_joint_blocks');
1302
+ const head_block_id = add_linked_chain(
1303
+ blocks_table,
1304
+ joints,
1305
+ JOINTS_PER_SKIN_BLOCK,
1306
+ (start, count, next) => {
1307
+ const inline_joints = new Array(JOINTS_PER_SKIN_BLOCK);
1308
+ for (let j = 0; j < JOINTS_PER_SKIN_BLOCK; j++) {
1309
+ inline_joints[j] = j < count ? joints[start + j] : ZERO_SKIN_JOINT;
1310
+ }
1311
+ return {
1312
+ joints: inline_joints,
1313
+ count,
1314
+ next,
1315
+ skin_matrix_offset: skin_matrix_offset + start,
1316
+ };
1317
+ }
1318
+ );
1319
+
1320
+ // Patch the head pointer onto the already-allocated skin
1321
+ // row. Read-modify-write so any other fields the user might
1322
+ // have changed via mutators (none today, but future-proof)
1323
+ // are preserved.
1324
+ const skins_table = this.#database.get('animation_skins');
1325
+ const current = skins_table.get(skin_id);
1326
+ skins_table.set(skin_id, {
1327
+ joint_block_head: head_block_id,
1328
+ joint_count: current.joint_count,
1329
+ });
1330
+
1331
+ return true;
1332
+ }
1333
+
1334
+ /**
1335
+ * High-level clip registration: consumes a {@link ShadeAnimationClip}
1336
+ * (CPU data with Node3D targets and per-component curves), uploads
1337
+ * curves and tracks, and creates the GPU clip record.
1338
+ *
1339
+ * **Eventual consistency.** The `clip_id` is allocated and
1340
+ * returned synchronously — callers can immediately call
1341
+ * {@link start} / {@link stop} / {@link set_time} /
1342
+ * {@link set_playback_rate} / {@link set_flags} / {@link clear_flags}
1343
+ * / {@link set_playback_weight} / {@link unregister_clip}. If
1344
+ * any channel target's Node3D hasn't been added to a scene yet
1345
+ * (no `id_mapping` row), the bindings chain that drives the
1346
+ * apply pass is queued and built on the next {@link update}
1347
+ * that sees all targets resolved. The clip's table row uses a
1348
+ * placeholder `bound_track_group_head` until then — apply
1349
+ * produces no contributions, so the clip is observationally
1350
+ * inert (still subject to time advancement via the tick shader
1351
+ * if `Playing` is set).
1352
+ *
1353
+ * Curves and tracks are interned synchronously since they're
1354
+ * pure data — no `id_mapping` dependency.
1355
+ *
1356
+ * @param {ShadeAnimationClip} clip
1357
+ * @returns {number} clip id
1358
+ */
1359
+ register_clip(clip) {
1360
+ assert.defined(clip, 'clip');
1361
+ assert.equal(clip.isShadeAnimationClip, true, 'clip.isShadeAnimationClip !== true');
1362
+
1363
+ const channels = clip.channels;
1364
+
1365
+ // Intern every channel's curves + track now. None of this
1366
+ // depends on id_mapping — pure data uploads.
1367
+ //
1368
+ // Each curve goes through #intern_curve so two channels
1369
+ // referencing the same AnimationCurve instance share one
1370
+ // GPU upload. With 324 dancers retargeted at the same base
1371
+ // clip this turns 324 × 195 channel uploads into 1 (the
1372
+ // curves are read-only animation data — identical bytes
1373
+ // wherever they're referenced).
1374
+ const pending_channels = new Array(channels.length);
1375
+ for (let i = 0; i < channels.length; i++) {
1376
+ const channel = channels[i];
1377
+ const curves = channel.curves;
1378
+ const track_channels = {};
1379
+ if (curves.x !== undefined) track_channels.x = this.#intern_curve(curves.x);
1380
+ if (curves.y !== undefined) track_channels.y = this.#intern_curve(curves.y);
1381
+ if (curves.z !== undefined) track_channels.z = this.#intern_curve(curves.z);
1382
+ if (curves.w !== undefined) track_channels.w = this.#intern_curve(curves.w);
1383
+
1384
+ pending_channels[i] = {
1385
+ track: this.#intern_track(track_channels),
1386
+ target: channel.target,
1387
+ property: channel.property,
1388
+ };
1389
+ }
1390
+
1391
+ // Reserve the clip row immediately with a placeholder head
1392
+ // pointer. State mutators and the public id are usable from
1393
+ // this point on; the bindings chain gets patched in once
1394
+ // id_mapping has every target. `time` defaults to 0 (the
1395
+ // playback offset relative to `time_start`; the apply
1396
+ // shader does `effective_time = clip.time + clip.time_start`).
1397
+ const clips_table = this.#database.get('animation_clips');
1398
+ const clip_id = clips_table.add({
1399
+ time: 0,
1400
+ time_start: clip.start_time,
1401
+ time_end: clip.end_time,
1402
+ bound_track_group_head: ANIMATION_BOUND_TRACK_GROUP_NULL,
1403
+ playback_rate: 1,
1404
+ flags: 0,
1405
+ playback_weight: 1,
1406
+ });
1407
+
1408
+ const pending = { clip_id, channels: pending_channels };
1409
+ if (!this.#try_complete_clip(pending)) {
1410
+ this.#pending_clips.push(pending);
1411
+ }
1412
+
1413
+ /*
1414
+ Asked cheaply first: a clip with no scale channel at all — which is every clip a character
1415
+ normally carries — is dismissed in one pass over its channels and remembered by nothing. Only
1416
+ a clip that scales something is worth holding against the skins, now and as more arrive.
1417
+ */
1418
+ if (clip_scale_channels(clip).length > 0) {
1419
+ this.#scaling_clips.set(clip_id, clip);
1420
+
1421
+ for (const skin of this.#skins_by_id.values()) {
1422
+ this.#report_scale_conflicts(skin, clip);
1423
+ }
1424
+ }
1425
+
1426
+ return clip_id;
1427
+ }
1428
+
1429
+ /**
1430
+ * Attempt to build the bindings chain for a pending clip and
1431
+ * patch its head pointer onto the clip row. Returns `true` when
1432
+ * every channel target has resolved to a transforms-table row
1433
+ * (and the chain was built); `false` when at least one is still
1434
+ * missing.
1435
+ *
1436
+ * @param {{
1437
+ * clip_id: number,
1438
+ * channels: Array<{ track: number, target: Node3D, property: number }>,
1439
+ * }} pending
1440
+ * @returns {boolean}
1441
+ */
1442
+ #try_complete_clip(pending) {
1443
+ const id_mapping = this.#scene_context.id_mapping;
1444
+ const channels = pending.channels;
1445
+ const clip_id = pending.clip_id;
1446
+
1447
+ const bindings = new Array(channels.length);
1448
+ for (let i = 0; i < channels.length; i++) {
1449
+ const ch = channels[i];
1450
+ const instance = id_mapping.get(ch.target.id);
1451
+ if (instance === undefined) return false;
1452
+
1453
+ bindings[i] = {
1454
+ track: ch.track,
1455
+ instance,
1456
+ property: ch.property,
1457
+ };
1458
+ }
1459
+
1460
+ const groups_table = this.#database.get('animation_bound_track_groups');
1461
+ const head_group_id = add_linked_chain(
1462
+ groups_table,
1463
+ bindings,
1464
+ TRACKS_PER_GROUP_NODE,
1465
+ (start, count, next) => {
1466
+ const inline_bindings = new Array(TRACKS_PER_GROUP_NODE);
1467
+ for (let j = 0; j < TRACKS_PER_GROUP_NODE; j++) {
1468
+ inline_bindings[j] = j < count ? bindings[start + j] : ZERO_BINDING;
1469
+ }
1470
+ return { bindings: inline_bindings, count, next, clip: clip_id };
1471
+ }
1472
+ );
1473
+
1474
+ // Patch the head pointer onto the already-allocated clip
1475
+ // row. Read-modify-write preserves any user-driven state
1476
+ // changes (set_time, set_flags, etc.) made between
1477
+ // register_clip and the drain.
1478
+ this.#patch_clip_field(clip_id, 'bound_track_group_head', head_group_id);
1479
+
1480
+ return true;
1481
+ }
1482
+
1483
+ /**
1484
+ * Drop a clip from the manager. Walks the bindings chain and
1485
+ * removes every group-node + the clip row. Tolerates the clip
1486
+ * still being pending (drains it from the queue without GPU
1487
+ * touches besides the original row removal).
1488
+ *
1489
+ * Curves and tracks the clip referenced stay alive — they're
1490
+ * shared via {@link #intern_curve} / {@link #intern_track} and
1491
+ * may be in use by other clips. Removing them would need
1492
+ * reference-counting the manager doesn't carry.
1493
+ *
1494
+ * Safe to call repeatedly on the same id: the second call finds the row unoccupied and
1495
+ * returns without walking anything.
1496
+ *
1497
+ * @param {number} clip_id
1498
+ */
1499
+ unregister_clip(clip_id) {
1500
+ assert.isNonNegativeInteger(clip_id, 'clip_id');
1501
+
1502
+ // Drop from pending queue if present (skip-and-keep-others
1503
+ // pattern is cheaper than splice for the rare case of
1504
+ // dozens-of-pending; if pending depth grows this can be
1505
+ // revisited).
1506
+ for (let i = this.#pending_clips.length - 1; i >= 0; i--) {
1507
+ if (this.#pending_clips[i].clip_id === clip_id) {
1508
+ this.#pending_clips.splice(i, 1);
1509
+ }
1510
+ }
1511
+
1512
+ const clips_table = this.#database.get('animation_clips');
1513
+ const groups_table = this.#database.get('animation_bound_track_groups');
1514
+
1515
+ // Occupancy rather than `get` — see {@link unregister_skin}. A removed row reads back as
1516
+ // zeros, and a zeroed `bound_track_group_head` is group 0, which the walk below would
1517
+ // chase into itself forever.
1518
+ if (!clips_table.occupancy.get(clip_id)) return;
1519
+
1520
+ const clip_record = clips_table.get(clip_id);
1521
+
1522
+ // Walk the bindings chain and remove every node. The chain
1523
+ // may be empty (placeholder head NULL) if the clip is still
1524
+ // pending or has been registered then drained but never
1525
+ // completed; both cases skip the loop.
1526
+ let block_id = clip_record.bound_track_group_head;
1527
+ while (block_id !== ANIMATION_BOUND_TRACK_GROUP_NULL) {
1528
+ const block = groups_table.get(block_id);
1529
+ const next_id = block !== undefined ? block.next : ANIMATION_BOUND_TRACK_GROUP_NULL;
1530
+ groups_table.remove(block_id);
1531
+ block_id = next_id;
1532
+ }
1533
+
1534
+ clips_table.remove(clip_id);
1535
+
1536
+ this.#scaling_clips.delete(clip_id);
1537
+ }
1538
+
1539
+ /**
1540
+ * Drop a skin from the manager. Walks the joint-block chain and
1541
+ * removes every block + the skin row, and tears down every
1542
+ * skinning binding pointing at this skin (so the skinning
1543
+ * context doesn't try to read the now-freed skin's matrix
1544
+ * offset on its next upload).
1545
+ *
1546
+ * The skin's matrix range goes back on the free list, for the next skin with the same
1547
+ * joint count to take as-is. A spawn/despawn loop over a fixed cast therefore costs
1548
+ * what the cast costs, not what the loop has run.
1549
+ *
1550
+ * Tolerates the skin still being pending. Safe to call repeatedly on the same id: the
1551
+ * second call finds the row unoccupied and gives no range back twice.
1552
+ *
1553
+ * @param {number} skin_id
1554
+ */
1555
+ unregister_skin(skin_id) {
1556
+ assert.isNonNegativeInteger(skin_id, 'skin_id');
1557
+
1558
+ this.#skins_by_id.delete(skin_id);
1559
+
1560
+ for (let i = this.#pending_skins.length - 1; i >= 0; i--) {
1561
+ if (this.#pending_skins[i].skin_id === skin_id) {
1562
+ this.#pending_skins.splice(i, 1);
1563
+ }
1564
+ }
1565
+
1566
+ // Tear down skinning bindings first so the skinning
1567
+ // context's next upload doesn't reference a freed skin row
1568
+ // (its upload path looks up skin_matrix_offset via
1569
+ // get_skin_matrix_offset, which would assert post-removal).
1570
+ this.#scene_context.skinning.unbind_all_by_skin(skin_id);
1571
+
1572
+ const skins_table = this.#database.get('animation_skins');
1573
+ const blocks_table = this.#database.get('animation_skin_joint_blocks');
1574
+
1575
+ // Occupancy, not `get`, is what says the row is still this skin's. `remove` zero-fills a
1576
+ // slot rather than dropping it and `get` reads those bytes back, so a second call would
1577
+ // find a record whose `joint_block_head` is 0 — a valid block id, not the NULL sentinel —
1578
+ // and the walk below would follow block 0 to block 0 for as long as the process lives.
1579
+ if (!skins_table.occupancy.get(skin_id)) {
1580
+ this.#skin_matrix_offsets.delete(skin_id);
1581
+ return;
1582
+ }
1583
+
1584
+ const skin_record = skins_table.get(skin_id);
1585
+
1586
+ // Walk the joint-block chain, terminating on the NULL
1587
+ // sentinel (also the placeholder for a not-yet-patched head).
1588
+ // Note: 0 is a VALID block row id — the first single-block
1589
+ // skin registered into an empty table gets head 0, and
1590
+ // treating it as a placeholder would leak its block, leaving
1591
+ // the prep shader processing it against a recycled
1592
+ // transforms-table row every frame.
1593
+ let block_id = skin_record.joint_block_head;
1594
+ while (block_id !== ANIMATION_SKIN_JOINT_BLOCK_NULL) {
1595
+ const block = blocks_table.get(block_id);
1596
+ const next_id = block !== undefined ? block.next : ANIMATION_SKIN_JOINT_BLOCK_NULL;
1597
+ blocks_table.remove(block_id);
1598
+ block_id = next_id;
1599
+ }
1600
+
1601
+ skins_table.remove(skin_id);
1602
+
1603
+ // Give the matrix range back while the record that says how long it is still exists.
1604
+ // Reaching here means the row was occupied, and a row is occupied only between the
1605
+ // registration that set this offset and the delete below — the occupancy check at the
1606
+ // top is what keeps a second call from handing the same base out twice.
1607
+ const skin_matrix_offset = this.#skin_matrix_offsets.get(skin_id);
1608
+
1609
+ assert.defined(skin_matrix_offset, `skin ${skin_id} holds a live row but no matrix offset`);
1610
+
1611
+ this.#free_skin_matrix_range(skin_matrix_offset, skin_record.joint_count);
1612
+
1613
+ this.#skin_matrix_offsets.delete(skin_id);
1614
+ }
1615
+
1616
+ /**
1617
+ * Cached wrapper around {@link add_curve}: one upload per distinct curve, where distinct means
1618
+ * distinct *content*.
1619
+ *
1620
+ * The instance cache answers first — the common case inside one clip, and free. Otherwise the
1621
+ * curve's keys are hashed and held against every uploaded curve with that hash; a match on
1622
+ * every key, tangents included, reuses that upload, and only a curve nobody has uploaded before
1623
+ * goes through the low-level path. Either way the instance is remembered, so the next look-up
1624
+ * of it is the free one.
1625
+ *
1626
+ * @param {AnimationCurve} curve
1627
+ * @returns {number} curve id (index into the `animation_curves` table)
1628
+ */
1629
+ #intern_curve(curve) {
1630
+ const cached = this.#curve_id_cache.get(curve);
1631
+
1632
+ if (cached !== undefined) {
1633
+ return cached;
1634
+ }
1635
+
1636
+ const hash = curve.hash();
1637
+
1638
+ let bucket = this.#curve_id_by_content.get(hash);
1639
+
1640
+ if (bucket === undefined) {
1641
+ bucket = [];
1642
+
1643
+ this.#curve_id_by_content.set(hash, bucket);
1644
+ } else {
1645
+ for (const entry of bucket) {
1646
+ if (entry.curve.equals(curve)) {
1647
+ this.#curve_id_cache.set(curve, entry.id);
1648
+
1649
+ return entry.id;
1650
+ }
1651
+ }
1652
+ }
1653
+
1654
+ const id = this.add_curve(curve);
1655
+
1656
+ this.#curve_id_cache.set(curve, id);
1657
+
1658
+ bucket.push({ curve, id });
1659
+
1660
+ return id;
1661
+ }
1662
+
1663
+ /**
1664
+ * Cache-keyed wrapper around {@link add_track}. Two channels that
1665
+ * end up with the same (x, y, z, w, mask) — typical when their
1666
+ * source curves are shared via {@link #intern_curve} — collapse
1667
+ * to one track row instead of one row per channel.
1668
+ *
1669
+ * @param {{ x?: number, y?: number, z?: number, w?: number }} channels
1670
+ * @returns {number} track id
1671
+ */
1672
+ #intern_track(channels) {
1673
+ // Pack into a deterministic string key. -1 sentinel for
1674
+ // missing channels keeps the (x=undefined) and (x=0) cases
1675
+ // distinct.
1676
+ const cx = channels.x ?? -1;
1677
+ const cy = channels.y ?? -1;
1678
+ const cz = channels.z ?? -1;
1679
+ const cw = channels.w ?? -1;
1680
+ const key = `${cx},${cy},${cz},${cw}`;
1681
+
1682
+ const cached = this.#track_id_cache.get(key);
1683
+ if (cached !== undefined) {
1684
+ return cached;
1685
+ }
1686
+ const id = this.add_track(channels);
1687
+ this.#track_id_cache.set(key, id);
1688
+ return id;
1689
+ }
1690
+
1691
+ /**
1692
+ * Reserve `joint_count` consecutive matrices and answer with the base index.
1693
+ *
1694
+ * A range of exactly that length that some skin has given back is preferred over fresh
1695
+ * space, which is what keeps a spawn/despawn loop from growing the buffer — see
1696
+ * {@link #free_skin_matrix_ranges} for why the match is exact and never split.
1697
+ *
1698
+ * @param {number} joint_count length of the range, in matrices
1699
+ * @returns {number} base matrix index of the reserved range
1700
+ */
1701
+ #allocate_skin_matrix_range(joint_count) {
1702
+ const reusable = this.#free_skin_matrix_ranges.get(joint_count);
1703
+
1704
+ if (reusable !== undefined && reusable.length > 0) {
1705
+ // Already inside the buffer — it was sized for this range when it was first
1706
+ // handed out, and the buffer only ever grows.
1707
+ return reusable.pop();
1708
+ }
1709
+
1710
+ const skin_matrix_offset = this.#next_skin_matrix_offset;
1711
+
1712
+ this.#next_skin_matrix_offset += joint_count;
1713
+ this.#ensure_skin_matrices_capacity(this.#next_skin_matrix_offset);
1714
+
1715
+ return skin_matrix_offset;
1716
+ }
1717
+
1718
+ /**
1719
+ * Put a matrix range back on the free list.
1720
+ *
1721
+ * The bytes are left as they are: the prep pass rewrites every matrix of a live skin each
1722
+ * frame, so what a dead skin left behind is only ever read by a pass that no longer has a
1723
+ * block pointing at it.
1724
+ *
1725
+ * @param {number} skin_matrix_offset base matrix index of the range
1726
+ * @param {number} joint_count length of the range, in matrices
1727
+ */
1728
+ #free_skin_matrix_range(skin_matrix_offset, joint_count) {
1729
+ let bucket = this.#free_skin_matrix_ranges.get(joint_count);
1730
+
1731
+ if (bucket === undefined) {
1732
+ bucket = [];
1733
+
1734
+ this.#free_skin_matrix_ranges.set(joint_count, bucket);
1735
+ }
1736
+
1737
+ bucket.push(skin_matrix_offset);
1738
+ }
1739
+
1740
+ /**
1741
+ * Grow the skinning-matrix scratch buffer if `required_count`
1742
+ * exceeds current capacity. Doubles on grow until the requirement
1743
+ * fits. The buffer's contents are scratch (rewritten every frame by
1744
+ * the prep pass), so growth doesn't need to copy old data — but it
1745
+ * does invalidate the previous {@link #skin_matrices_buffer}
1746
+ * reference, which is why callers re-fetch through the getter.
1747
+ *
1748
+ * @param {number} required_count number of matrices that must fit
1749
+ */
1750
+ #ensure_skin_matrices_capacity(required_count) {
1751
+ if (required_count <= this.#skin_matrices_capacity) {
1752
+ return;
1753
+ }
1754
+
1755
+ let new_capacity = Math.max(this.#skin_matrices_capacity, SKIN_MATRICES_INITIAL_CAPACITY);
1756
+ while (new_capacity < required_count) {
1757
+ new_capacity *= 2;
1758
+ }
1759
+
1760
+ // Grow both ping-pong buffers in lockstep — they always share
1761
+ // capacity. After grow both are zero-initialised, so the next
1762
+ // prep dispatch needs to seed "previous" from "current"
1763
+ // (otherwise velocity would interpolate against zero
1764
+ // matrices). The flag below tells dispatch_skin_matrix_prep
1765
+ // to issue that copy.
1766
+ for (let i = 0; i < 2; i++) {
1767
+ if (this.#skin_matrices_buffers[i] !== null) {
1768
+ this.#skin_matrices_buffers[i].destroy();
1769
+ }
1770
+ this.#skin_matrices_buffers[i] = this.#device.createBuffer({
1771
+ label: `${this.#label}/SkinMatrices/${i}`,
1772
+ size: new_capacity * SKIN_MATRIX_BYTES,
1773
+ usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
1774
+ });
1775
+ }
1776
+ this.#skin_matrices_capacity = new_capacity;
1777
+ this.#first_frame_skin_matrices = true;
1778
+ }
1779
+
1780
+ /**
1781
+ * Flush all pending uploads (curves, keyframe blocks, tracks, clips,
1782
+ * bound-track groups, skins, skin joint blocks) into the GPU
1783
+ * database. Call once per frame, before any shader that reads from
1784
+ * `database.buffer`.
1785
+ *
1786
+ * Also drains the pending-registration queues: skins and clips
1787
+ * whose dependencies (joint Node3D rows, channel-target rows)
1788
+ * weren't ready at register time get one shot per `update` to
1789
+ * complete. Once `id_mapping` has the missing rows, the
1790
+ * deferred chain build runs and the registration becomes
1791
+ * observationally complete — the next-frame `database.update`
1792
+ * picks up the patched head pointer.
1793
+ *
1794
+ * Does not touch the skin-matrices ping-pong buffers — those
1795
+ * are scratch populated each frame by
1796
+ * {@link dispatch_skin_matrix_prep}, not host data.
1797
+ *
1798
+ * @param {ShadeGPUCommandContext} cmd_ctx
1799
+ */
1800
+ update(cmd_ctx) {
1801
+ this.#drain_pending_skins();
1802
+ this.#drain_pending_clips();
1803
+ this.#database.update(cmd_ctx);
1804
+ }
1805
+
1806
+ #drain_pending_skins() {
1807
+ // Walk back-to-front so successful completions can splice
1808
+ // without disturbing the iteration's remaining indices.
1809
+ for (let i = this.#pending_skins.length - 1; i >= 0; i--) {
1810
+ if (this.#try_complete_skin(this.#pending_skins[i])) {
1811
+ this.#pending_skins.splice(i, 1);
1812
+ }
1813
+ }
1814
+ }
1815
+
1816
+ #drain_pending_clips() {
1817
+ for (let i = this.#pending_clips.length - 1; i >= 0; i--) {
1818
+ if (this.#try_complete_clip(this.#pending_clips[i])) {
1819
+ this.#pending_clips.splice(i, 1);
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ /**
1825
+ * Run the per-frame skin-matrix-prep dispatch and advance the
1826
+ * ping-pong. Replaces the manual `shader_skin_matrix_prep.dispatch`
1827
+ * the prototypes used to call — wrapping it here is how we get
1828
+ * correct ping-pong behaviour across frames without leaking the
1829
+ * buffer-role bookkeeping into every caller.
1830
+ *
1831
+ * No-op when no skins are registered (`group_count_x === 0`).
1832
+ *
1833
+ * Per-frame ordering:
1834
+ * 1. Flip {@link #skin_matrices_current_index} — the buffer that
1835
+ * was last frame's "current" is about to be overwritten with
1836
+ * this frame's matrices; the other buffer (last frame's prev)
1837
+ * becomes this frame's "previous" implicit data source for
1838
+ * {@link prev_skin_matrices_buffer}.
1839
+ * 2. Wait — clarify: we want last frame's "current" to BECOME
1840
+ * this frame's "previous". So flipping current_index moves
1841
+ * the role tag the correct way: what was current[i] becomes
1842
+ * previous (= buffers[1 - i]), and current[1 - i] is the
1843
+ * write target.
1844
+ * 3. Dispatch the prep shader into the new current.
1845
+ * 4. On the very first frame (or after a grow), copy current →
1846
+ * previous so the first velocity reads see matching matrices
1847
+ * and produce zero velocity.
1848
+ *
1849
+ * @param {ShadeGPUCommandContext} cmd_ctx
1850
+ */
1851
+ dispatch_skin_matrix_prep(cmd_ctx) {
1852
+ const groups = this.#database
1853
+ .get('animation_skin_joint_blocks')
1854
+ .dispatch_group_count(SKIN_MATRIX_PREP_WORKGROUP_SIZE);
1855
+ if (groups === 0) {
1856
+ return;
1857
+ }
1858
+
1859
+ // Flip the ping-pong: what was "current" last frame is no
1860
+ // longer current, but it still holds valid matrices — and
1861
+ // through the role flip it becomes this frame's
1862
+ // `prev_skin_matrices_buffer`. The buffer we're flipping TO
1863
+ // (the new "current") held two-frames-old matrices, about to
1864
+ // be overwritten.
1865
+ this.#skin_matrices_current_index = 1 - this.#skin_matrices_current_index;
1866
+
1867
+ const current = this.#skin_matrices_buffers[this.#skin_matrices_current_index];
1868
+ const previous = this.#skin_matrices_buffers[1 - this.#skin_matrices_current_index];
1869
+
1870
+ shader_skin_matrix_prep.dispatch({
1871
+ encoder: cmd_ctx,
1872
+ group_count_x: groups,
1873
+ bindings: {
1874
+ animation_database: this.#database.buffer,
1875
+ scene_database: this.#scene_context.scene_database_buffer,
1876
+ skin_matrices: current,
1877
+ },
1878
+ });
1879
+
1880
+ // First-frame init (or post-grow): "previous" buffer is
1881
+ // zero-initialised, which would have the velocity pass
1882
+ // interpolate against the origin and produce huge nonsense
1883
+ // on the first frame the skin appears. Seed prev from
1884
+ // current — first velocity reads then yield zero, which is
1885
+ // the right answer (no motion has happened yet).
1886
+ if (this.#first_frame_skin_matrices) {
1887
+ cmd_ctx.copyBufferToBuffer(
1888
+ current, 0,
1889
+ previous, 0,
1890
+ current.size,
1891
+ );
1892
+ this.#first_frame_skin_matrices = false;
1893
+ }
1894
+ }
1895
+
1896
+ /**
1897
+ * Per-frame orchestrator: drains pending registrations and the
1898
+ * database queue, runs the GPU tick (advances `clip.time`),
1899
+ * applies tracks into the pose accumulator, resolves the
1900
+ * accumulator into node TRS, propagates the transform
1901
+ * hierarchy, preps skinning matrices, and runs the skinning
1902
+ * compute pass that deforms cloned vertex regions in place.
1903
+ *
1904
+ * One call replaces the ~80-line per-frame dispatch block every
1905
+ * consumer would otherwise duplicate; the shader identities,
1906
+ * workgroup sizes, pose-accumulator sizing, and pass ordering
1907
+ * are all internal.
1908
+ *
1909
+ * `dt_seconds` is wall-clock seconds since the last call (the
1910
+ * caller measures via `performance.now()` or equivalent). It's
1911
+ * clamped to {@link MAX_DT_SECONDS} to keep a stall from
1912
+ * leaking multiple seconds into the next tick.
1913
+ *
1914
+ * Clip tick/apply/resolve gate on bound tracks; hierarchy propagation runs
1915
+ * every frame, before skin matrix prep and skinning. CPU-driven nodes need it
1916
+ * too: scene uploads seed global with the previous pose, and propagation
1917
+ * composes the current one. On idle frames it advances prev_global so motion
1918
+ * vectors settle to zero. Skin passes still gate on registered skins.
1919
+ *
1920
+ * @param {ShadeGPUCommandContext} cmd_ctx
1921
+ * @param {number} dt_seconds wall-clock seconds since last call
1922
+ */
1923
+ tick(cmd_ctx, dt_seconds) {
1924
+ assert.defined(cmd_ctx, 'cmd_ctx');
1925
+ assert.isNumber(dt_seconds, 'dt_seconds');
1926
+
1927
+ // Cap dt so a long stall doesn't fly the clip through dozens
1928
+ // of loops or clamp it past the meaningful range.
1929
+ const dt = Math.min(dt_seconds, MAX_DT_SECONDS);
1930
+
1931
+ // Flush database + drain pending registrations.
1932
+ this.update(cmd_ctx);
1933
+
1934
+ // Only clip evaluation is conditional. Every scene needs current globals.
1935
+ const apply_groups = this.#database
1936
+ .get('animation_bound_track_groups')
1937
+ .dispatch_group_count(ANIMATION_APPLY_WORKGROUP_SIZE);
1938
+
1939
+ if (apply_groups > 0) {
1940
+ this.#encode_clip_passes(cmd_ctx, dt, apply_groups);
1941
+ } else {
1942
+ // CPU uploads seed global with the previous pose. Even without clips,
1943
+ // compose current globals and advance prev_global before skinning/rendering.
1944
+ const graph = new FrameGraph("GPUAnimationManager/TransformHierarchy");
1945
+ graph_scene_update_transform_hierarchy({ graph, scene: this.#scene_context });
1946
+ cmd_ctx.encodeGraph(graph);
1947
+ }
1948
+
1949
+ // Skin matrix prep + skinning deformation. Both no-op when
1950
+ // no skins are registered — and both run independent of the
1951
+ // bound-track gate above: a registered skin whose joints
1952
+ // are host-driven (no clips bound) must still be deformed
1953
+ // each frame, otherwise it renders bind pose forever.
1954
+ this.dispatch_skin_matrix_prep(cmd_ctx);
1955
+ this.#scene_context.skinning.update(cmd_ctx);
1956
+ }
1957
+
1958
+ /**
1959
+ * Encode the clip-driven passes: GPU tick (advances
1960
+ * `clip.time`), track apply into the pose accumulator, pose
1961
+ * resolve into node TRS, and transform-hierarchy propagation.
1962
+ * Split out of {@link tick} so the skin passes there run
1963
+ * independently of the bound-track early-out.
1964
+ *
1965
+ * @param {ShadeGPUCommandContext} cmd_ctx
1966
+ * @param {number} dt clamped delta time, seconds
1967
+ * @param {number} apply_groups dispatch group count for the
1968
+ * apply pass — caller guarantees `> 0`
1969
+ */
1970
+ #encode_clip_passes(cmd_ctx, dt, apply_groups) {
1971
+ // Tick: advance clip.time on the GPU.
1972
+ const tick_groups = this.#database
1973
+ .get('animation_clips')
1974
+ .dispatch_group_count(ANIMATION_TICK_WORKGROUP_SIZE);
1975
+ if (tick_groups > 0) {
1976
+ shader_animation_tick.dispatch({
1977
+ encoder: cmd_ctx,
1978
+ group_count_x: tick_groups,
1979
+ bindings: {
1980
+ dt,
1981
+ animation_database: this.#database.buffer,
1982
+ },
1983
+ });
1984
+ }
1985
+
1986
+ // Size the pose accumulator to cover every allocated row of
1987
+ // the transforms table — apply writes by absolute row index,
1988
+ // resolve iterates the table via page iterator.
1989
+ const transforms_table = this.#scene_context.scene_database.get('transforms');
1990
+ const resolve_groups = transforms_table.dispatch_group_count(
1991
+ ANIMATION_POSE_RESOLVE_WORKGROUP_SIZE
1992
+ );
1993
+ this.ensure_pose_accumulator_capacity(transforms_table.element_capacity);
1994
+
1995
+ // ── Frame graph: apply → pose resolve → hierarchy propagate
1996
+ const graph = new FrameGraph("GPUAnimationManager/Tick");
1997
+ const gr_animation_database = graph_import_buffer(
1998
+ graph, this.#database.buffer, "animation_database"
1999
+ );
2000
+ const gr_scene_database = graph_import_buffer(
2001
+ graph, this.#scene_context.scene_database_buffer, "scene_database"
2002
+ );
2003
+ const gr_materials = graph_import_buffer(
2004
+ graph, this.#scene_context.materials.metadata_table.buffer, "materials"
2005
+ );
2006
+ const gr_pose_accumulator = graph_import_buffer(
2007
+ graph, this.#pose_accumulator_buffer, "pose_accumulator"
2008
+ );
2009
+
2010
+ const apply_data = {};
2011
+ const apply_pass = graph.add("animation/apply", apply_data, (data, resources, ctx) => {
2012
+ shader_animation_apply.dispatch({
2013
+ encoder: ctx.encoder,
2014
+ group_count_x: apply_groups,
2015
+ bindings: {
2016
+ animation_database: resources.get(data.animation_database),
2017
+ pose_accumulator: resources.get(data.pose_accumulator),
2018
+ materials: resources.get(data.materials),
2019
+ },
2020
+ });
2021
+ });
2022
+ apply_data.animation_database = apply_pass.read(gr_animation_database);
2023
+ apply_data.materials = apply_pass.write(apply_pass.read(gr_materials));
2024
+ apply_data.pose_accumulator = apply_pass.write(apply_pass.read(gr_pose_accumulator));
2025
+
2026
+ const resolve_data = {};
2027
+ const resolve_pass = graph.add("animation/pose_resolve", resolve_data, (data, resources, ctx) => {
2028
+ if (resolve_groups === 0) return;
2029
+ shader_animation_pose_resolve.dispatch({
2030
+ encoder: ctx.encoder,
2031
+ group_count_x: resolve_groups,
2032
+ bindings: {
2033
+ scene_database: resources.get(data.scene_database),
2034
+ pose_accumulator: resources.get(data.pose_accumulator),
2035
+ },
2036
+ });
2037
+ });
2038
+ resolve_data.scene_database = resolve_pass.write(resolve_pass.read(gr_scene_database));
2039
+ resolve_data.pose_accumulator = resolve_pass.write(resolve_pass.read(apply_data.pose_accumulator));
2040
+
2041
+ graph_scene_update_transform_hierarchy({
2042
+ graph,
2043
+ scene: this.#scene_context,
2044
+ scene_database: resolve_data.scene_database,
2045
+ });
2046
+
2047
+ cmd_ctx.encodeGraph(graph);
2048
+ }
2049
+
2050
+ destroy() {
2051
+ this.#database.destroy();
2052
+ for (let i = 0; i < 2; i++) {
2053
+ if (this.#skin_matrices_buffers[i] !== null) {
2054
+ this.#skin_matrices_buffers[i].destroy();
2055
+ this.#skin_matrices_buffers[i] = null;
2056
+ }
2057
+ }
2058
+ if (this.#pose_accumulator_buffer !== null) {
2059
+ this.#pose_accumulator_buffer.destroy();
2060
+ this.#pose_accumulator_buffer = null;
2061
+ }
2062
+ }
2063
+ }