@haiyue/extensions 0.1.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 (293) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/animation/Animation2DComponent.d.ts +46 -0
  4. package/dist/animation/Animation2DExtensionRegistry.d.ts +27 -0
  5. package/dist/animation/Animation2DRenderSystem.d.ts +89 -0
  6. package/dist/animation/Animation2DRuntime.d.ts +99 -0
  7. package/dist/animation/Animation2DStateMachine.d.ts +13 -0
  8. package/dist/animation/Animation2DStateMachineComponent.d.ts +54 -0
  9. package/dist/animation/Animation2DStateMachineRuntime.d.ts +30 -0
  10. package/dist/animation/Animation2DStateMachineSystem.d.ts +14 -0
  11. package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +27 -0
  12. package/dist/animation/Animation2DSystem.d.ts +14 -0
  13. package/dist/animation/AnimationAssetLoader.d.ts +9 -0
  14. package/dist/animation/AnimationAudioClip.d.ts +26 -0
  15. package/dist/animation/AnimationPathTessellator.d.ts +52 -0
  16. package/dist/animation/AnimationTextRasterizer.d.ts +21 -0
  17. package/dist/animation/AnimationVisual2D.d.ts +68 -0
  18. package/dist/animation/HyaAnimation2DClipAdapter.d.ts +16 -0
  19. package/dist/animation/Particle2DDescriptorAdapter.d.ts +5 -0
  20. package/dist/animation/runtime/mixer/Action.d.ts +150 -0
  21. package/dist/animation/runtime/mixer/Mixer.d.ts +54 -0
  22. package/dist/animation/runtime/mixer/PoseBuffer.d.ts +23 -0
  23. package/dist/animation/runtime/mixer/Sampler.d.ts +17 -0
  24. package/dist/animation/runtime/mixer/Types.d.ts +101 -0
  25. package/dist/animation/runtime/mixer/index.d.ts +6 -0
  26. package/dist/animation-state-machine/AnimationStateMachine.d.ts +111 -0
  27. package/dist/animation-state-machine/AnimationStateMachineChannels.d.ts +23 -0
  28. package/dist/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.d.ts +51 -0
  29. package/dist/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.d.ts +14 -0
  30. package/dist/animation-state-machine/runtime/AnimationStateMachineCompiler.d.ts +109 -0
  31. package/dist/animation-state-machine/runtime/AnimationStateMachineController.d.ts +103 -0
  32. package/dist/animation-state-machine/runtime/AnimationStateMachineMixerPort.d.ts +35 -0
  33. package/dist/animation-state-machine/runtime/index.d.ts +7 -0
  34. package/dist/animation.d.ts +15 -0
  35. package/dist/animation.js +1171 -0
  36. package/dist/animation3d/Animation3DAction.d.ts +46 -0
  37. package/dist/animation3d/Animation3DBinding.d.ts +85 -0
  38. package/dist/animation3d/Animation3DClip.d.ts +20 -0
  39. package/dist/animation3d/Animation3DError.d.ts +13 -0
  40. package/dist/animation3d/Animation3DMixer.d.ts +27 -0
  41. package/dist/animation3d/Animation3DPose.d.ts +40 -0
  42. package/dist/animation3d/Animation3DPoseBuffer.d.ts +24 -0
  43. package/dist/animation3d/Animation3DResource.d.ts +28 -0
  44. package/dist/animation3d/Animation3DStateMachine.d.ts +95 -0
  45. package/dist/animation3d/Animation3DStateMachineRuntime.d.ts +70 -0
  46. package/dist/animation3d/Animation3DTrack.d.ts +19 -0
  47. package/dist/animation3d/hya/HyaAnimation3DRuntime.d.ts +73 -0
  48. package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +65 -0
  49. package/dist/animation3d/hya/HyaAnimation3DTypes.d.ts +170 -0
  50. package/dist/animation3d/hya/index.d.ts +4 -0
  51. package/dist/animation3d/index.d.ts +14 -0
  52. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +81 -0
  53. package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +39 -0
  54. package/dist/animation3d/runtime/integration/index.d.ts +2 -0
  55. package/dist/animation3d/runtime/mixer/Action.d.ts +150 -0
  56. package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +6 -0
  57. package/dist/animation3d/runtime/mixer/Mixer.d.ts +59 -0
  58. package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +1 -0
  59. package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +24 -0
  60. package/dist/animation3d/runtime/mixer/index.d.ts +4 -0
  61. package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +1 -0
  62. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +1 -0
  63. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +1 -0
  64. package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +1 -0
  65. package/dist/animation3d/runtime/state-machine/index.d.ts +6 -0
  66. package/dist/animation3d.d.ts +5 -0
  67. package/dist/animation3d.js +1708 -0
  68. package/dist/benchmark.d.ts +30 -0
  69. package/dist/benchmark.js +119 -0
  70. package/dist/canvas-text/CanvasText2DRenderSystem.d.ts +24 -0
  71. package/dist/canvas-text/CanvasTextComponent.d.ts +18 -0
  72. package/dist/canvas-text.d.ts +4 -0
  73. package/dist/canvas-text.js +276 -0
  74. package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +4 -0
  75. package/dist/chunks/2d-ui-canvas-text-2d.generated-C0Eaej2m.js +4 -0
  76. package/dist/chunks/2d-ui-spine2d.generated-CUUi8xjl.js +4 -0
  77. package/dist/chunks/2d-ui-tilemap2d.generated-gebw4mX7.js +4 -0
  78. package/dist/chunks/Animation2DStateMachine-9IonguNa.js +3245 -0
  79. package/dist/chunks/AnimationStateMachineController-BIcAh-sT.js +1438 -0
  80. package/dist/chunks/GltfAnimation3DAdapter-Cu3rQQsW.js +2240 -0
  81. package/dist/chunks/GltfAnimationRuntime-CRHsLycN.js +631 -0
  82. package/dist/chunks/RenderSystem2DBase-DDT61dAu.js +227 -0
  83. package/dist/chunks/Spine2DRuntime-D9M9LAQR.js +3056 -0
  84. package/dist/chunks/SpineAssetParser-BSDmPge1.js +378 -0
  85. package/dist/chunks/gltfLoader-Dm22jLYB.js +2085 -0
  86. package/dist/chunks/pluginUtils-CvwroRMd.js +33 -0
  87. package/dist/experimental-gltf-worker.d.ts +4 -0
  88. package/dist/experimental-gltf-worker.js +138 -0
  89. package/dist/experimental-spine-worker.d.ts +4 -0
  90. package/dist/experimental-spine-worker.js +110 -0
  91. package/dist/gltf/GltfAccessorReader.d.ts +7 -0
  92. package/dist/gltf/GltfAnimation3DAdapter.d.ts +47 -0
  93. package/dist/gltf/GltfAnimationRuntime.d.ts +10 -0
  94. package/dist/gltf/GltfAssetWorkerClient.d.ts +20 -0
  95. package/dist/gltf/GltfCompatibilityReport.d.ts +70 -0
  96. package/dist/gltf/GltfConservativeBounds.d.ts +18 -0
  97. package/dist/gltf/GltfDracoDecoder.d.ts +4 -0
  98. package/dist/gltf/GltfExtensionAdapter.d.ts +54 -0
  99. package/dist/gltf/GltfLoaderContract.d.ts +249 -0
  100. package/dist/gltf/GltfLoaderErrors.d.ts +4 -0
  101. package/dist/gltf/GltfMaterialDescriptor.d.ts +23 -0
  102. package/dist/gltf/GltfMaterialEncoder.d.ts +20 -0
  103. package/dist/gltf/GltfMaterialLoader.d.ts +6 -0
  104. package/dist/gltf/GltfModelComponent.d.ts +72 -0
  105. package/dist/gltf/GltfModelSystem.d.ts +37 -0
  106. package/dist/gltf/GltfPlugin.d.ts +6 -0
  107. package/dist/gltf/GltfSchema.d.ts +225 -0
  108. package/dist/gltf/GltfUvSemanticPlanner.d.ts +33 -0
  109. package/dist/gltf/gltfLoader.d.ts +9 -0
  110. package/dist/gltf-animation3d.d.ts +3 -0
  111. package/dist/gltf-animation3d.js +8 -0
  112. package/dist/gltf-worker-runtime.d.ts +7 -0
  113. package/dist/gltf-worker-runtime.js +6981 -0
  114. package/dist/gltf.d.ts +12 -0
  115. package/dist/gltf.js +517 -0
  116. package/dist/grid/Grid2DComponent.d.ts +25 -0
  117. package/dist/grid.d.ts +2 -0
  118. package/dist/grid.js +56 -0
  119. package/dist/hya-state-machine.d.ts +7 -0
  120. package/dist/hya-state-machine.js +2934 -0
  121. package/dist/index.d.ts +2 -0
  122. package/dist/index.js +7 -0
  123. package/dist/internal/2d-ui-shader-artifact.js +870 -0
  124. package/dist/plugins/pluginUtils.d.ts +20 -0
  125. package/dist/plugins.d.ts +6 -0
  126. package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +658 -0
  127. package/dist/spine/Spine2DComponent.d.ts +116 -0
  128. package/dist/spine/Spine2DGpuRenderer.d.ts +65 -0
  129. package/dist/spine/Spine2DRenderSystem.d.ts +27 -0
  130. package/dist/spine/Spine2DRuntime.d.ts +103 -0
  131. package/dist/spine/SpineAnimationBoneTimelines.d.ts +8 -0
  132. package/dist/spine/SpineAssetParser.d.ts +18 -0
  133. package/dist/spine/SpineAssetWorkerClient.d.ts +19 -0
  134. package/dist/spine/SpineAssetWorkerContract.d.ts +6 -0
  135. package/dist/spine/SpineAtlasParser.d.ts +21 -0
  136. package/dist/spine/SpineFloatBuilder.d.ts +15 -0
  137. package/dist/spine/SpinePathConstraintSolver.d.ts +32 -0
  138. package/dist/spine/SpinePlugin.d.ts +8 -0
  139. package/dist/spine/SpineSkeletonRuntime.d.ts +185 -0
  140. package/dist/spine/SpineSlotAnimation.d.ts +17 -0
  141. package/dist/spine/SpineTimelineRuntime.d.ts +25 -0
  142. package/dist/spine/SpineVertexBuilder.d.ts +61 -0
  143. package/dist/spine.d.ts +7 -0
  144. package/dist/spine.js +578 -0
  145. package/dist/tilemap/Tilemap2DComponent.d.ts +110 -0
  146. package/dist/tilemap/Tilemap2DRenderSystem.d.ts +11 -0
  147. package/dist/tilemap/Tilemap2DRenderer.d.ts +24 -0
  148. package/dist/tilemap/TilemapPlugin.d.ts +8 -0
  149. package/dist/tilemap.d.ts +6 -0
  150. package/dist/tilemap.js +535 -0
  151. package/dist/tween/Tween2DComponent.d.ts +70 -0
  152. package/dist/tween/Tween2DSystem.d.ts +12 -0
  153. package/dist/tween.d.ts +4 -0
  154. package/dist/tween.js +138 -0
  155. package/dist/utils/RenderSystem2DBase.d.ts +42 -0
  156. package/dist/utils/arrayAccess.d.ts +4 -0
  157. package/dist/utils/camera2d.d.ts +12 -0
  158. package/dist/utils/render2dGpu.d.ts +32 -0
  159. package/package.json +124 -0
  160. package/src/animation/Animation2DComponent.ts +135 -0
  161. package/src/animation/Animation2DExtensionRegistry.ts +52 -0
  162. package/src/animation/Animation2DRenderSystem.ts +1026 -0
  163. package/src/animation/Animation2DRuntime.ts +1326 -0
  164. package/src/animation/Animation2DStateMachine.ts +39 -0
  165. package/src/animation/Animation2DStateMachineComponent.ts +228 -0
  166. package/src/animation/Animation2DStateMachineRuntime.ts +141 -0
  167. package/src/animation/Animation2DStateMachineSystem.ts +60 -0
  168. package/src/animation/Animation2DStateMachineVisualRuntime.ts +282 -0
  169. package/src/animation/Animation2DSystem.ts +72 -0
  170. package/src/animation/AnimationAssetLoader.ts +84 -0
  171. package/src/animation/AnimationAudioClip.ts +140 -0
  172. package/src/animation/AnimationPathTessellator.ts +691 -0
  173. package/src/animation/AnimationTextRasterizer.ts +340 -0
  174. package/src/animation/AnimationVisual2D.ts +153 -0
  175. package/src/animation/HyaAnimation2DClipAdapter.ts +845 -0
  176. package/src/animation/Particle2DDescriptorAdapter.ts +35 -0
  177. package/src/animation/runtime/mixer/Action.ts +876 -0
  178. package/src/animation/runtime/mixer/Mixer.ts +505 -0
  179. package/src/animation/runtime/mixer/PoseBuffer.ts +105 -0
  180. package/src/animation/runtime/mixer/Sampler.ts +252 -0
  181. package/src/animation/runtime/mixer/Types.ts +138 -0
  182. package/src/animation/runtime/mixer/index.ts +31 -0
  183. package/src/animation-state-machine/AnimationStateMachine.ts +146 -0
  184. package/src/animation-state-machine/AnimationStateMachineChannels.ts +116 -0
  185. package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +197 -0
  186. package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +503 -0
  187. package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +763 -0
  188. package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +987 -0
  189. package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +54 -0
  190. package/src/animation-state-machine/runtime/index.ts +47 -0
  191. package/src/animation.ts +33 -0
  192. package/src/animation3d/Animation3DAction.ts +73 -0
  193. package/src/animation3d/Animation3DBinding.ts +116 -0
  194. package/src/animation3d/Animation3DClip.ts +22 -0
  195. package/src/animation3d/Animation3DError.ts +34 -0
  196. package/src/animation3d/Animation3DMixer.ts +82 -0
  197. package/src/animation3d/Animation3DPose.ts +100 -0
  198. package/src/animation3d/Animation3DPoseBuffer.ts +85 -0
  199. package/src/animation3d/Animation3DResource.ts +49 -0
  200. package/src/animation3d/Animation3DStateMachine.ts +126 -0
  201. package/src/animation3d/Animation3DStateMachineRuntime.ts +191 -0
  202. package/src/animation3d/Animation3DTrack.ts +29 -0
  203. package/src/animation3d/hya/HyaAnimation3DRuntime.ts +756 -0
  204. package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +618 -0
  205. package/src/animation3d/hya/HyaAnimation3DTypes.ts +168 -0
  206. package/src/animation3d/hya/index.ts +10 -0
  207. package/src/animation3d/index.ts +76 -0
  208. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +325 -0
  209. package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +158 -0
  210. package/src/animation3d/runtime/integration/index.ts +9 -0
  211. package/src/animation3d/runtime/mixer/Action.ts +833 -0
  212. package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +26 -0
  213. package/src/animation3d/runtime/mixer/Mixer.ts +562 -0
  214. package/src/animation3d/runtime/mixer/PoseBuffer.ts +1 -0
  215. package/src/animation3d/runtime/mixer/TrackSampler.ts +399 -0
  216. package/src/animation3d/runtime/mixer/index.ts +8 -0
  217. package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +4 -0
  218. package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +1 -0
  219. package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +1 -0
  220. package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +1 -0
  221. package/src/animation3d/runtime/state-machine/index.ts +45 -0
  222. package/src/animation3d.ts +5 -0
  223. package/src/benchmark.ts +168 -0
  224. package/src/canvas-text/CanvasText2DRenderSystem.ts +292 -0
  225. package/src/canvas-text/CanvasTextComponent.ts +47 -0
  226. package/src/canvas-text.ts +4 -0
  227. package/src/experimental-gltf-worker.ts +18 -0
  228. package/src/experimental-spine-worker.ts +9 -0
  229. package/src/gltf/GltfAccessorReader.ts +271 -0
  230. package/src/gltf/GltfAnimation3DAdapter.ts +680 -0
  231. package/src/gltf/GltfAnimationRuntime.ts +392 -0
  232. package/src/gltf/GltfAssetWorkerClient.ts +136 -0
  233. package/src/gltf/GltfCompatibilityReport.ts +214 -0
  234. package/src/gltf/GltfConservativeBounds.ts +219 -0
  235. package/src/gltf/GltfDracoDecoder.ts +251 -0
  236. package/src/gltf/GltfExtensionAdapter.ts +340 -0
  237. package/src/gltf/GltfLoaderContract.ts +333 -0
  238. package/src/gltf/GltfLoaderErrors.ts +59 -0
  239. package/src/gltf/GltfMaterialDescriptor.ts +134 -0
  240. package/src/gltf/GltfMaterialEncoder.ts +173 -0
  241. package/src/gltf/GltfMaterialLoader.ts +293 -0
  242. package/src/gltf/GltfModelComponent.ts +115 -0
  243. package/src/gltf/GltfModelSystem.ts +208 -0
  244. package/src/gltf/GltfPlugin.ts +84 -0
  245. package/src/gltf/GltfSchema.ts +229 -0
  246. package/src/gltf/GltfUvSemanticPlanner.ts +166 -0
  247. package/src/gltf/gltfLoader.ts +910 -0
  248. package/src/gltf-animation3d.ts +10 -0
  249. package/src/gltf-worker-runtime.ts +10 -0
  250. package/src/gltf.ts +56 -0
  251. package/src/grid/Grid2DComponent.ts +69 -0
  252. package/src/grid.ts +2 -0
  253. package/src/hya-state-machine.ts +19 -0
  254. package/src/index.ts +2 -0
  255. package/src/plugins/pluginUtils.ts +50 -0
  256. package/src/plugins.ts +6 -0
  257. package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +242 -0
  258. package/src/shaders/generated/2d-ui-artifact.generated.ts +867 -0
  259. package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +38 -0
  260. package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +41 -0
  261. package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +36 -0
  262. package/src/spine/Spine2DComponent.ts +168 -0
  263. package/src/spine/Spine2DGpuRenderer.ts +449 -0
  264. package/src/spine/Spine2DRenderSystem.ts +199 -0
  265. package/src/spine/Spine2DRuntime.ts +1529 -0
  266. package/src/spine/SpineAnimationBoneTimelines.ts +30 -0
  267. package/src/spine/SpineAssetParser.ts +20 -0
  268. package/src/spine/SpineAssetWorkerClient.ts +112 -0
  269. package/src/spine/SpineAssetWorkerContract.ts +4 -0
  270. package/src/spine/SpineAtlasParser.ts +127 -0
  271. package/src/spine/SpineFloatBuilder.ts +66 -0
  272. package/src/spine/SpinePathConstraintSolver.ts +582 -0
  273. package/src/spine/SpinePlugin.ts +76 -0
  274. package/src/spine/SpineSkeletonRuntime.ts +521 -0
  275. package/src/spine/SpineSlotAnimation.ts +195 -0
  276. package/src/spine/SpineTimelineRuntime.ts +453 -0
  277. package/src/spine/SpineVertexBuilder.ts +686 -0
  278. package/src/spine.ts +7 -0
  279. package/src/tilemap/Tilemap2DComponent.ts +232 -0
  280. package/src/tilemap/Tilemap2DRenderSystem.ts +58 -0
  281. package/src/tilemap/Tilemap2DRenderer.ts +289 -0
  282. package/src/tilemap/TilemapPlugin.ts +75 -0
  283. package/src/tilemap.ts +6 -0
  284. package/src/tween/Tween2DComponent.ts +83 -0
  285. package/src/tween/Tween2DSystem.ts +78 -0
  286. package/src/tween.ts +4 -0
  287. package/src/types/earcut.d.ts +7 -0
  288. package/src/types/wgsl.d.ts +4 -0
  289. package/src/utils/RenderSystem2DBase.ts +164 -0
  290. package/src/utils/arrayAccess.ts +17 -0
  291. package/src/utils/camera2d.ts +68 -0
  292. package/src/utils/render2dGpu.ts +132 -0
  293. package/test/fixtures/gltf/animation-characterization.gltf +344 -0
@@ -0,0 +1,987 @@
1
+ import {
2
+ evaluateAnimation3DBlend1DWeights,
3
+ evaluateAnimation3DBlend2DWeights,
4
+ } from './AnimationStateMachineBlendTreeWeights.js';
5
+ import type {
6
+ CompiledAnimation3DCondition,
7
+ CompiledAnimation3DLayer,
8
+ CompiledAnimation3DMotion,
9
+ CompiledAnimation3DParameter,
10
+ CompiledAnimation3DState,
11
+ CompiledAnimation3DStateMachine,
12
+ CompiledAnimation3DTransition,
13
+ } from './AnimationStateMachineCompiler.js';
14
+ import type {
15
+ Animation3DStateMachineActionHandle,
16
+ Animation3DStateMachineMixerPort,
17
+ } from './AnimationStateMachineMixerPort.js';
18
+
19
+ const TIME_EPSILON = 1e-9;
20
+ const DEFAULT_MAX_TRANSITIONS_PER_UPDATE = 128;
21
+ const CONTROLLER_UPDATE_TRANSACTION = Symbol('AnimationStateMachineController.updateTransaction');
22
+
23
+ export type Animation3DStateMachineControllerStatus =
24
+ | 'active'
25
+ | 'destroyed';
26
+
27
+ export interface Animation3DStateMachineControllerOptions {
28
+ /**
29
+ * Bounds zero-duration transition cycles and pathological large-delta
30
+ * updates per layer. Remaining time is still advanced after the limit is
31
+ * reached.
32
+ */
33
+ readonly maxTransitionsPerUpdate?: number;
34
+ }
35
+
36
+ export interface Animation3DStateMachineLayerSnapshot {
37
+ readonly layerId: string;
38
+ readonly currentStateId: string;
39
+ readonly currentTime: number;
40
+ readonly transitionId: string | null;
41
+ readonly sourceStateId: string | null;
42
+ readonly destinationStateId: string | null;
43
+ readonly transitionProgress: number;
44
+ }
45
+
46
+ interface RuntimeClip<
47
+ TAction extends Animation3DStateMachineActionHandle,
48
+ > {
49
+ readonly kind: 'clip';
50
+ readonly action: TAction;
51
+ }
52
+
53
+ interface RuntimeBlend1D<
54
+ TAction extends Animation3DStateMachineActionHandle,
55
+ > {
56
+ readonly kind: 'blend-1d';
57
+ readonly motion: Extract<CompiledAnimation3DMotion, { readonly kind: 'blend-1d' }>;
58
+ readonly children: readonly RuntimeMotion<TAction>[];
59
+ readonly weights: Float64Array;
60
+ }
61
+
62
+ interface RuntimeBlend2D<
63
+ TAction extends Animation3DStateMachineActionHandle,
64
+ > {
65
+ readonly kind: 'blend-2d';
66
+ readonly motion: Extract<CompiledAnimation3DMotion, { readonly kind: 'blend-2d' }>;
67
+ readonly children: readonly RuntimeMotion<TAction>[];
68
+ readonly weights: Float64Array;
69
+ }
70
+
71
+ type RuntimeMotion<
72
+ TAction extends Animation3DStateMachineActionHandle,
73
+ > =
74
+ | RuntimeClip<TAction>
75
+ | RuntimeBlend1D<TAction>
76
+ | RuntimeBlend2D<TAction>;
77
+
78
+ interface StatePlayback<
79
+ TAction extends Animation3DStateMachineActionHandle,
80
+ > {
81
+ readonly state: CompiledAnimation3DState;
82
+ readonly motion: RuntimeMotion<TAction>;
83
+ time: number;
84
+ }
85
+
86
+ interface ActiveTransition<
87
+ TAction extends Animation3DStateMachineActionHandle,
88
+ > {
89
+ readonly definition: CompiledAnimation3DTransition;
90
+ readonly source: StatePlayback<TAction>;
91
+ readonly destination: StatePlayback<TAction>;
92
+ elapsed: number;
93
+ }
94
+
95
+ interface LayerRuntime<
96
+ TAction extends Animation3DStateMachineActionHandle,
97
+ > {
98
+ readonly definition: CompiledAnimation3DLayer;
99
+ current: StatePlayback<TAction>;
100
+ transition: ActiveTransition<TAction> | null;
101
+ }
102
+
103
+ interface LayerTransactionCheckpoint<
104
+ TAction extends Animation3DStateMachineActionHandle,
105
+ > {
106
+ current: StatePlayback<TAction>;
107
+ transition: ActiveTransition<TAction> | null;
108
+ currentTime: number;
109
+ sourceTime: number;
110
+ destinationTime: number;
111
+ transitionElapsed: number;
112
+ }
113
+
114
+ interface TransactionalAnimationStateMachineMixerPort {
115
+ beginControllerTransaction(): void;
116
+ commitControllerTransaction(): void;
117
+ rollbackControllerTransaction(): void;
118
+ }
119
+
120
+ interface InterruptionCandidate<
121
+ TAction extends Animation3DStateMachineActionHandle,
122
+ > {
123
+ readonly transition: CompiledAnimation3DTransition;
124
+ readonly anchor: StatePlayback<TAction>;
125
+ }
126
+
127
+ /**
128
+ * Deterministic, mixer-agnostic runtime controller for a compiled definition.
129
+ * All string resolution happens in the compiler or at setter/query boundaries.
130
+ */
131
+ export class Animation3DStateMachineController<
132
+ TAction extends Animation3DStateMachineActionHandle =
133
+ Animation3DStateMachineActionHandle,
134
+ > {
135
+ readonly compiled: CompiledAnimation3DStateMachine;
136
+ readonly port: Animation3DStateMachineMixerPort<TAction>;
137
+ readonly maxTransitionsPerUpdate: number;
138
+
139
+ lastUpdateTransitionCount = 0;
140
+ transitionLimitReached = false;
141
+
142
+ private readonly _parameterValues: Float64Array;
143
+ private readonly _transactionParameterValues: Float64Array;
144
+ private readonly _layers: LayerRuntime<TAction>[] = [];
145
+ private readonly _layerTransactionCheckpoints: LayerTransactionCheckpoint<TAction>[] = [];
146
+ private _transactionLastUpdateTransitionCount = 0;
147
+ private _transactionLimitReached = false;
148
+ private _transactionActive = false;
149
+ private _status: Animation3DStateMachineControllerStatus = 'active';
150
+
151
+ constructor(
152
+ compiled: CompiledAnimation3DStateMachine,
153
+ port: Animation3DStateMachineMixerPort<TAction>,
154
+ options: Animation3DStateMachineControllerOptions = {},
155
+ ) {
156
+ const maxTransitions =
157
+ options.maxTransitionsPerUpdate ?? DEFAULT_MAX_TRANSITIONS_PER_UPDATE;
158
+ if (!Number.isSafeInteger(maxTransitions) || maxTransitions <= 0) {
159
+ throw new RangeError('maxTransitionsPerUpdate must be a positive safe integer.');
160
+ }
161
+ this.compiled = compiled;
162
+ this.port = port;
163
+ this.maxTransitionsPerUpdate = maxTransitions;
164
+ this._parameterValues = new Float64Array(compiled.parameters.length);
165
+ this._transactionParameterValues = new Float64Array(compiled.parameters.length);
166
+ this._restoreParameterDefaults();
167
+ try {
168
+ for (const layer of compiled.layers) {
169
+ const current = this._createPlayback(
170
+ layer,
171
+ layer.states[layer.initialStateIndex]!,
172
+ 0,
173
+ );
174
+ this._layers.push({ definition: layer, current, transition: null });
175
+ }
176
+ this._synchronizeAllLayers();
177
+ } catch (error) {
178
+ this._destroyAllLayers();
179
+ this._status = 'destroyed';
180
+ throw error;
181
+ }
182
+ }
183
+
184
+ get status(): Animation3DStateMachineControllerStatus {
185
+ return this._status;
186
+ }
187
+
188
+ get layerSnapshots(): readonly Animation3DStateMachineLayerSnapshot[] {
189
+ return Object.freeze(this._layers.map(layer => this._snapshotLayer(layer)));
190
+ }
191
+
192
+ getLayerSnapshot(layerId: string): Animation3DStateMachineLayerSnapshot {
193
+ const layerIndex = this.compiled.layerIndexById.get(layerId);
194
+ if (layerIndex === undefined) throw new ReferenceError(`Unknown layer "${layerId}".`);
195
+ const runtime = this._layers[layerIndex];
196
+ if (!runtime) throw new Error('State-machine controller is destroyed.');
197
+ return this._snapshotLayer(runtime);
198
+ }
199
+
200
+ setFloat(name: string, value: number): this {
201
+ if (!Number.isFinite(value)) throw new RangeError('Float parameters must be finite.');
202
+ this._setTypedParameter(name, 'float', value);
203
+ return this;
204
+ }
205
+
206
+ setInteger(name: string, value: number): this {
207
+ if (!Number.isSafeInteger(value)) {
208
+ throw new RangeError('Integer parameters must be safe integers.');
209
+ }
210
+ this._setTypedParameter(name, 'integer', value);
211
+ return this;
212
+ }
213
+
214
+ setBoolean(name: string, value: boolean): this {
215
+ if (typeof value !== 'boolean') throw new TypeError('Boolean parameters require a boolean.');
216
+ this._setTypedParameter(name, 'boolean', value ? 1 : 0);
217
+ return this;
218
+ }
219
+
220
+ setTrigger(name: string): this {
221
+ this._setTypedParameter(name, 'trigger', 1);
222
+ return this;
223
+ }
224
+
225
+ resetTrigger(name: string): this {
226
+ this._setTypedParameter(name, 'trigger', 0);
227
+ return this;
228
+ }
229
+
230
+ getParameter(name: string): number | boolean {
231
+ const parameter = this._resolveParameter(name);
232
+ const value = this._parameterValues[parameter.index]!;
233
+ return parameter.type === 'boolean' || parameter.type === 'trigger'
234
+ ? value !== 0
235
+ : value;
236
+ }
237
+
238
+ update(deltaSeconds: number): this {
239
+ this[CONTROLLER_UPDATE_TRANSACTION](deltaSeconds);
240
+ return this;
241
+ }
242
+
243
+ /** @internal Keeps controller and transactional mixer ports atomic through final pose evaluation. */
244
+ [CONTROLLER_UPDATE_TRANSACTION](
245
+ deltaSeconds: number,
246
+ beforeUpdate?: () => void,
247
+ complete?: () => unknown,
248
+ ): unknown {
249
+ this._requireActive();
250
+ if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) {
251
+ throw new RangeError('State-machine deltaSeconds must be finite and non-negative.');
252
+ }
253
+ if (this._transactionActive) {
254
+ throw new Error('State-machine controller update transaction is already active.');
255
+ }
256
+
257
+ const transactionalPort = resolveTransactionalPort(this.port);
258
+ this._captureUpdateTransaction();
259
+ this._transactionActive = true;
260
+ let portTransactionActive = false;
261
+ try {
262
+ if (transactionalPort) {
263
+ transactionalPort.beginControllerTransaction();
264
+ portTransactionActive = true;
265
+ }
266
+ beforeUpdate?.();
267
+ this.lastUpdateTransitionCount = 0;
268
+ this.transitionLimitReached = false;
269
+ for (const layer of this._layers) this._updateLayer(layer, deltaSeconds);
270
+ const result = complete?.();
271
+ transactionalPort?.commitControllerTransaction();
272
+ portTransactionActive = false;
273
+ this._transactionActive = false;
274
+ return result;
275
+ } catch (error) {
276
+ this._restoreUpdateTransaction();
277
+ this._transactionActive = false;
278
+ if (portTransactionActive) {
279
+ try {
280
+ transactionalPort!.rollbackControllerTransaction();
281
+ } catch (rollbackError) {
282
+ throw new AggregateError(
283
+ [error, rollbackError],
284
+ 'State-machine update and rollback both failed.',
285
+ );
286
+ }
287
+ }
288
+ throw error;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Restores defaults and initial states. Repeated reset calls are safe and
294
+ * leave the same observable controller state.
295
+ */
296
+ reset(): this {
297
+ if (this._status === 'destroyed') return this;
298
+ this._destroyAllLayers();
299
+ this._restoreParameterDefaults();
300
+ this.lastUpdateTransitionCount = 0;
301
+ this.transitionLimitReached = false;
302
+ for (const layer of this.compiled.layers) {
303
+ const current = this._createPlayback(
304
+ layer,
305
+ layer.states[layer.initialStateIndex]!,
306
+ 0,
307
+ );
308
+ this._layers.push({ definition: layer, current, transition: null });
309
+ }
310
+ this._synchronizeAllLayers();
311
+ return this;
312
+ }
313
+
314
+ /** Stops and releases all port actions. Idempotent. */
315
+ destroy(): void {
316
+ if (this._status === 'destroyed') return;
317
+ this._destroyAllLayers();
318
+ this._status = 'destroyed';
319
+ }
320
+
321
+ private _updateLayer(layer: LayerRuntime<TAction>, deltaSeconds: number): void {
322
+ let remaining = deltaSeconds;
323
+ let layerTransitionCount = 0;
324
+ let inspectImmediateTransitions = true;
325
+ while (remaining > TIME_EPSILON || inspectImmediateTransitions) {
326
+ inspectImmediateTransitions = false;
327
+ if (layerTransitionCount >= this.maxTransitionsPerUpdate) {
328
+ this.transitionLimitReached = true;
329
+ this._advanceWithoutTransitions(layer, remaining);
330
+ remaining = 0;
331
+ break;
332
+ }
333
+
334
+ if (layer.transition) {
335
+ const interruption = this._findInterruption(layer);
336
+ if (interruption) {
337
+ this._interruptTransition(layer, interruption);
338
+ layerTransitionCount++;
339
+ this.lastUpdateTransitionCount++;
340
+ inspectImmediateTransitions = true;
341
+ continue;
342
+ }
343
+ const active = layer.transition;
344
+ const timeToCompletion = Math.max(
345
+ 0,
346
+ active.definition.duration - active.elapsed,
347
+ );
348
+ if (timeToCompletion <= TIME_EPSILON) {
349
+ this._completeTransition(layer);
350
+ inspectImmediateTransitions = true;
351
+ continue;
352
+ }
353
+ if (remaining <= TIME_EPSILON) break;
354
+ const step = Math.min(remaining, timeToCompletion);
355
+ this._advancePlayback(active.source, step);
356
+ this._advancePlayback(active.destination, step);
357
+ active.elapsed = Math.min(
358
+ active.definition.duration,
359
+ active.elapsed + step,
360
+ );
361
+ remaining = Math.max(0, remaining - step);
362
+ this._synchronizeLayer(layer);
363
+ if (
364
+ active.elapsed + TIME_EPSILON >= active.definition.duration
365
+ && layer.transition === active
366
+ ) {
367
+ this._completeTransition(layer);
368
+ inspectImmediateTransitions = true;
369
+ }
370
+ continue;
371
+ }
372
+
373
+ const candidate = this._findEligibleTransition(
374
+ layer.definition,
375
+ layer.current,
376
+ -1,
377
+ );
378
+ if (candidate) {
379
+ this._startTransition(layer, layer.current, candidate);
380
+ layerTransitionCount++;
381
+ this.lastUpdateTransitionCount++;
382
+ inspectImmediateTransitions = true;
383
+ continue;
384
+ }
385
+ if (remaining <= TIME_EPSILON) break;
386
+ const wait = this._timeUntilNextExitTransition(layer);
387
+ const step = Number.isFinite(wait)
388
+ ? Math.min(remaining, Math.max(wait, TIME_EPSILON))
389
+ : remaining;
390
+ this._advancePlayback(layer.current, step);
391
+ remaining = Math.max(0, remaining - step);
392
+ this._synchronizeLayer(layer);
393
+ if (Number.isFinite(wait) && step + TIME_EPSILON >= wait) {
394
+ inspectImmediateTransitions = true;
395
+ }
396
+ }
397
+ this._synchronizeLayer(layer);
398
+ }
399
+
400
+ private _captureUpdateTransaction(): void {
401
+ this._transactionParameterValues.set(this._parameterValues);
402
+ this._transactionLastUpdateTransitionCount = this.lastUpdateTransitionCount;
403
+ this._transactionLimitReached = this.transitionLimitReached;
404
+ for (let index = 0; index < this._layers.length; index++) {
405
+ const layer = this._layers[index]!;
406
+ const transition = layer.transition;
407
+ let checkpoint = this._layerTransactionCheckpoints[index];
408
+ if (!checkpoint) {
409
+ checkpoint = {
410
+ current: layer.current,
411
+ transition,
412
+ currentTime: 0,
413
+ sourceTime: 0,
414
+ destinationTime: 0,
415
+ transitionElapsed: 0,
416
+ };
417
+ this._layerTransactionCheckpoints.push(checkpoint);
418
+ }
419
+ checkpoint.current = layer.current;
420
+ checkpoint.transition = transition;
421
+ checkpoint.currentTime = layer.current.time;
422
+ checkpoint.sourceTime = transition?.source.time ?? 0;
423
+ checkpoint.destinationTime = transition?.destination.time ?? 0;
424
+ checkpoint.transitionElapsed = transition?.elapsed ?? 0;
425
+ }
426
+ }
427
+
428
+ private _restoreUpdateTransaction(): void {
429
+ this._parameterValues.set(this._transactionParameterValues);
430
+ this.lastUpdateTransitionCount = this._transactionLastUpdateTransitionCount;
431
+ this.transitionLimitReached = this._transactionLimitReached;
432
+ for (let index = 0; index < this._layers.length; index++) {
433
+ const layer = this._layers[index]!;
434
+ const checkpoint = this._layerTransactionCheckpoints[index]!;
435
+ layer.current = checkpoint.current;
436
+ layer.transition = checkpoint.transition;
437
+ checkpoint.current.time = checkpoint.currentTime;
438
+ if (checkpoint.transition) {
439
+ checkpoint.transition.source.time = checkpoint.sourceTime;
440
+ checkpoint.transition.destination.time = checkpoint.destinationTime;
441
+ checkpoint.transition.elapsed = checkpoint.transitionElapsed;
442
+ }
443
+ }
444
+ }
445
+
446
+ private _findEligibleTransition(
447
+ layer: CompiledAnimation3DLayer,
448
+ playback: StatePlayback<TAction>,
449
+ excludedTransitionIndex: number,
450
+ ): CompiledAnimation3DTransition | null {
451
+ for (const transition of layer.transitions) {
452
+ if (transition.index === excludedTransitionIndex) continue;
453
+ if (
454
+ transition.fromStateIndex !== -1
455
+ && transition.fromStateIndex !== playback.state.index
456
+ ) continue;
457
+ if (!this._conditionsPass(transition.conditions)) continue;
458
+ if (
459
+ transition.hasExitTime
460
+ && !this._exitTimeReached(playback, transition.exitTime)
461
+ ) continue;
462
+ return transition;
463
+ }
464
+ return null;
465
+ }
466
+
467
+ private _findInterruption(
468
+ layer: LayerRuntime<TAction>,
469
+ ): InterruptionCandidate<TAction> | null {
470
+ const active = layer.transition!;
471
+ const strategy = active.definition.interruption;
472
+ if (strategy === 'none') return null;
473
+ const source = (): InterruptionCandidate<TAction> | null => {
474
+ const transition = this._findEligibleTransition(
475
+ layer.definition,
476
+ active.source,
477
+ active.definition.index,
478
+ );
479
+ return transition ? { transition, anchor: active.source } : null;
480
+ };
481
+ const destination = (): InterruptionCandidate<TAction> | null => {
482
+ const transition = this._findEligibleTransition(
483
+ layer.definition,
484
+ active.destination,
485
+ active.definition.index,
486
+ );
487
+ return transition ? { transition, anchor: active.destination } : null;
488
+ };
489
+ if (strategy === 'source') return source();
490
+ if (strategy === 'destination') return destination();
491
+ if (strategy === 'source-then-destination') return source() ?? destination();
492
+ return destination() ?? source();
493
+ }
494
+
495
+ private _startTransition(
496
+ layer: LayerRuntime<TAction>,
497
+ source: StatePlayback<TAction>,
498
+ transition: CompiledAnimation3DTransition,
499
+ ): void {
500
+ const destinationState = layer.definition.states[transition.toStateIndex]!;
501
+ const destination = this._createPlayback(
502
+ layer.definition,
503
+ destinationState,
504
+ transition.destinationOffset,
505
+ );
506
+ // Consume only after destination action creation succeeds and the
507
+ // transition can actually become active.
508
+ this._consumeTriggers(transition);
509
+ const active: ActiveTransition<TAction> = {
510
+ definition: transition,
511
+ source,
512
+ destination,
513
+ elapsed: 0,
514
+ };
515
+ layer.transition = active;
516
+ this._fadePlayback(source, 0, transition.duration);
517
+ this._fadePlayback(
518
+ destination,
519
+ layer.definition.weight,
520
+ transition.duration,
521
+ );
522
+ this._synchronizeLayer(layer);
523
+ if (transition.duration <= TIME_EPSILON) this._completeTransition(layer);
524
+ }
525
+
526
+ private _interruptTransition(
527
+ layer: LayerRuntime<TAction>,
528
+ candidate: InterruptionCandidate<TAction>,
529
+ ): void {
530
+ const active = layer.transition!;
531
+ const discarded = candidate.anchor === active.source
532
+ ? active.destination
533
+ : active.source;
534
+ this._destroyPlayback(discarded);
535
+ layer.current = candidate.anchor;
536
+ layer.transition = null;
537
+ this._startTransition(layer, candidate.anchor, candidate.transition);
538
+ }
539
+
540
+ private _completeTransition(layer: LayerRuntime<TAction>): void {
541
+ const active = layer.transition;
542
+ if (!active) return;
543
+ this._destroyPlayback(active.source);
544
+ layer.current = active.destination;
545
+ layer.transition = null;
546
+ this._synchronizeLayer(layer);
547
+ }
548
+
549
+ private _timeUntilNextExitTransition(layer: LayerRuntime<TAction>): number {
550
+ let earliest = Infinity;
551
+ for (const transition of layer.definition.transitions) {
552
+ if (
553
+ transition.fromStateIndex !== -1
554
+ && transition.fromStateIndex !== layer.current.state.index
555
+ ) continue;
556
+ if (!transition.hasExitTime || !this._conditionsPass(transition.conditions)) continue;
557
+ earliest = Math.min(
558
+ earliest,
559
+ this._timeUntilExit(layer.current, transition.exitTime),
560
+ );
561
+ }
562
+ return earliest;
563
+ }
564
+
565
+ private _exitTimeReached(
566
+ playback: StatePlayback<TAction>,
567
+ exitTime: number,
568
+ ): boolean {
569
+ const duration = this._playbackDuration(playback);
570
+ if (duration <= TIME_EPSILON) return true;
571
+ const normalizedTime = playback.time / duration;
572
+ if (playback.state.loop === 'once' || exitTime >= 1) {
573
+ return normalizedTime + TIME_EPSILON >= exitTime;
574
+ }
575
+ const cycleTime = normalizedTime - Math.floor(normalizedTime);
576
+ return cycleTime + TIME_EPSILON >= exitTime;
577
+ }
578
+
579
+ private _timeUntilExit(
580
+ playback: StatePlayback<TAction>,
581
+ exitTime: number,
582
+ ): number {
583
+ if (this._exitTimeReached(playback, exitTime)) return 0;
584
+ const duration = this._playbackDuration(playback);
585
+ if (duration <= TIME_EPSILON) return 0;
586
+ const speed = this._playbackSpeed(playback);
587
+ if (speed <= TIME_EPSILON) return Infinity;
588
+ const normalizedTime = playback.time / duration;
589
+ let targetNormalized: number;
590
+ if (playback.state.loop === 'once' || exitTime >= 1) {
591
+ targetNormalized = exitTime;
592
+ } else {
593
+ targetNormalized = Math.floor(normalizedTime) + exitTime;
594
+ if (targetNormalized <= normalizedTime + TIME_EPSILON) targetNormalized += 1;
595
+ }
596
+ return Math.max(0, (targetNormalized * duration - playback.time) / speed);
597
+ }
598
+
599
+ private _conditionsPass(
600
+ conditions: readonly CompiledAnimation3DCondition[],
601
+ ): boolean {
602
+ for (const condition of conditions) {
603
+ const actual = this._parameterValues[condition.parameterIndex]!;
604
+ const expected = condition.value;
605
+ if (condition.operator === 'greater' && !(actual > expected)) return false;
606
+ if (
607
+ condition.operator === 'greater-or-equal'
608
+ && !(actual >= expected)
609
+ ) return false;
610
+ if (condition.operator === 'less' && !(actual < expected)) return false;
611
+ if (
612
+ condition.operator === 'less-or-equal'
613
+ && !(actual <= expected)
614
+ ) return false;
615
+ if (condition.operator === 'equal' && actual !== expected) return false;
616
+ if (condition.operator === 'not-equal' && actual === expected) return false;
617
+ if (condition.operator === 'is-true' && actual === 0) return false;
618
+ if (condition.operator === 'is-false' && actual !== 0) return false;
619
+ if (condition.operator === 'triggered' && actual === 0) return false;
620
+ }
621
+ return true;
622
+ }
623
+
624
+ private _consumeTriggers(transition: CompiledAnimation3DTransition): void {
625
+ for (const parameterIndex of transition.triggerParameterIndices) {
626
+ this._parameterValues[parameterIndex] = 0;
627
+ }
628
+ }
629
+
630
+ private _createPlayback(
631
+ layer: CompiledAnimation3DLayer,
632
+ state: CompiledAnimation3DState,
633
+ normalizedOffset: number,
634
+ ): StatePlayback<TAction> {
635
+ const createdActions: TAction[] = [];
636
+ try {
637
+ const motion = this._createRuntimeMotion(layer, state, state.motion, createdActions);
638
+ const playback: StatePlayback<TAction> = { state, motion, time: 0 };
639
+ playback.time = normalizedOffset * this._playbackDuration(playback);
640
+ this._forEachAction(motion, action => {
641
+ this.port.setWeight(action, 0);
642
+ this.port.setTime(action, playback.time);
643
+ this.port.setTimeScale(action, this._playbackSpeed(playback));
644
+ this.port.play(action);
645
+ });
646
+ return playback;
647
+ } catch (error) {
648
+ for (let index = createdActions.length - 1; index >= 0; index--) {
649
+ const action = createdActions[index]!;
650
+ this.port.stop(action);
651
+ this.port.destroyAction(action);
652
+ }
653
+ throw error;
654
+ }
655
+ }
656
+
657
+ private _createRuntimeMotion(
658
+ layer: CompiledAnimation3DLayer,
659
+ state: CompiledAnimation3DState,
660
+ motion: CompiledAnimation3DMotion,
661
+ createdActions: TAction[],
662
+ ): RuntimeMotion<TAction> {
663
+ if (motion.kind === 'clip') {
664
+ const action = this.port.createAction(motion.clipId, {
665
+ layerId: layer.id,
666
+ stateId: state.id,
667
+ loop: state.loop,
668
+ blendMode: layer.blendMode,
669
+ mask: layer.mask,
670
+ });
671
+ if (!Number.isFinite(action.duration) || action.duration < 0) {
672
+ throw new RangeError(
673
+ `Mixer port returned an invalid duration for clip "${motion.clipId}".`,
674
+ );
675
+ }
676
+ createdActions.push(action);
677
+ return { kind: 'clip', action };
678
+ }
679
+ const children = motion.children.map(child =>
680
+ this._createRuntimeMotion(layer, state, child.motion, createdActions));
681
+ return motion.kind === 'blend-1d'
682
+ ? {
683
+ kind: 'blend-1d',
684
+ motion,
685
+ children,
686
+ weights: new Float64Array(children.length),
687
+ }
688
+ : {
689
+ kind: 'blend-2d',
690
+ motion,
691
+ children,
692
+ weights: new Float64Array(children.length),
693
+ };
694
+ }
695
+
696
+ private _advancePlayback(
697
+ playback: StatePlayback<TAction>,
698
+ deltaSeconds: number,
699
+ ): void {
700
+ playback.time += deltaSeconds * this._playbackSpeed(playback);
701
+ }
702
+
703
+ private _advanceWithoutTransitions(
704
+ layer: LayerRuntime<TAction>,
705
+ deltaSeconds: number,
706
+ ): void {
707
+ if (deltaSeconds <= TIME_EPSILON) return;
708
+ const active = layer.transition;
709
+ if (!active) {
710
+ this._advancePlayback(layer.current, deltaSeconds);
711
+ this._synchronizeLayer(layer);
712
+ return;
713
+ }
714
+ const timeToCompletion = Math.max(0, active.definition.duration - active.elapsed);
715
+ const transitionStep = Math.min(deltaSeconds, timeToCompletion);
716
+ this._advancePlayback(active.source, transitionStep);
717
+ this._advancePlayback(active.destination, transitionStep);
718
+ active.elapsed += transitionStep;
719
+ const afterCompletion = deltaSeconds - transitionStep;
720
+ if (active.elapsed + TIME_EPSILON >= active.definition.duration) {
721
+ this._completeTransition(layer);
722
+ if (afterCompletion > TIME_EPSILON) {
723
+ this._advancePlayback(layer.current, afterCompletion);
724
+ }
725
+ }
726
+ this._synchronizeLayer(layer);
727
+ }
728
+
729
+ private _synchronizeAllLayers(): void {
730
+ for (const layer of this._layers) this._synchronizeLayer(layer);
731
+ }
732
+
733
+ private _synchronizeLayer(layer: LayerRuntime<TAction>): void {
734
+ const active = layer.transition;
735
+ if (!active) {
736
+ this._applyPlayback(layer.current, layer.definition.weight);
737
+ return;
738
+ }
739
+ const progress = active.definition.duration <= TIME_EPSILON
740
+ ? 1
741
+ : Math.max(0, Math.min(1, active.elapsed / active.definition.duration));
742
+ this._applyPlayback(
743
+ active.source,
744
+ layer.definition.weight * (1 - progress),
745
+ );
746
+ this._applyPlayback(
747
+ active.destination,
748
+ layer.definition.weight * progress,
749
+ );
750
+ }
751
+
752
+ private _applyPlayback(
753
+ playback: StatePlayback<TAction>,
754
+ parentWeight: number,
755
+ ): void {
756
+ const speed = this._playbackSpeed(playback);
757
+ this._applyMotion(
758
+ playback.motion,
759
+ parentWeight,
760
+ playback.time,
761
+ speed,
762
+ );
763
+ }
764
+
765
+ private _applyMotion(
766
+ motion: RuntimeMotion<TAction>,
767
+ parentWeight: number,
768
+ time: number,
769
+ timeScale: number,
770
+ ): void {
771
+ if (motion.kind === 'clip') {
772
+ this.port.setWeight(motion.action, parentWeight);
773
+ this.port.setTime(motion.action, time);
774
+ this.port.setTimeScale(motion.action, timeScale);
775
+ return;
776
+ }
777
+ this._evaluateDirectWeights(motion);
778
+ for (let index = 0; index < motion.children.length; index++) {
779
+ this._applyMotion(
780
+ motion.children[index]!,
781
+ parentWeight * motion.weights[index]!,
782
+ time,
783
+ timeScale,
784
+ );
785
+ }
786
+ }
787
+
788
+ private _playbackDuration(playback: StatePlayback<TAction>): number {
789
+ return this._motionDuration(playback.motion);
790
+ }
791
+
792
+ private _motionDuration(motion: RuntimeMotion<TAction>): number {
793
+ if (motion.kind === 'clip') return motion.action.duration;
794
+ this._evaluateDirectWeights(motion);
795
+ let duration = 0;
796
+ for (let index = 0; index < motion.children.length; index++) {
797
+ duration += motion.weights[index]! * this._motionDuration(motion.children[index]!);
798
+ }
799
+ return duration;
800
+ }
801
+
802
+ private _playbackSpeed(playback: StatePlayback<TAction>): number {
803
+ const parameterScale = playback.state.speedParameterIndex < 0
804
+ ? 1
805
+ : this._parameterValues[playback.state.speedParameterIndex]!;
806
+ return playback.state.speed * parameterScale;
807
+ }
808
+
809
+ private _evaluateDirectWeights(
810
+ motion: RuntimeBlend1D<TAction> | RuntimeBlend2D<TAction>,
811
+ ): void {
812
+ if (motion.kind === 'blend-1d') {
813
+ evaluateAnimation3DBlend1DWeights(
814
+ motion.motion,
815
+ this._parameterValues[motion.motion.parameterIndex]!,
816
+ motion.weights,
817
+ );
818
+ } else {
819
+ evaluateAnimation3DBlend2DWeights(
820
+ motion.motion,
821
+ this._parameterValues[motion.motion.parameterXIndex]!,
822
+ this._parameterValues[motion.motion.parameterYIndex]!,
823
+ motion.weights,
824
+ );
825
+ }
826
+ }
827
+
828
+ private _fadePlayback(
829
+ playback: StatePlayback<TAction>,
830
+ targetParentWeight: number,
831
+ durationSeconds: number,
832
+ ): void {
833
+ this._fadeMotion(playback.motion, targetParentWeight, durationSeconds);
834
+ }
835
+
836
+ private _fadeMotion(
837
+ motion: RuntimeMotion<TAction>,
838
+ parentWeight: number,
839
+ durationSeconds: number,
840
+ ): void {
841
+ if (motion.kind === 'clip') {
842
+ this.port.fade(motion.action, parentWeight, durationSeconds);
843
+ return;
844
+ }
845
+ this._evaluateDirectWeights(motion);
846
+ for (let index = 0; index < motion.children.length; index++) {
847
+ this._fadeMotion(
848
+ motion.children[index]!,
849
+ parentWeight * motion.weights[index]!,
850
+ durationSeconds,
851
+ );
852
+ }
853
+ }
854
+
855
+ private _destroyPlayback(playback: StatePlayback<TAction>): void {
856
+ this._forEachAction(playback.motion, action => {
857
+ this.port.stop(action);
858
+ this.port.destroyAction(action);
859
+ });
860
+ }
861
+
862
+ private _destroyAllLayers(): void {
863
+ for (const layer of this._layers) {
864
+ if (layer.transition) {
865
+ this._destroyPlayback(layer.transition.source);
866
+ this._destroyPlayback(layer.transition.destination);
867
+ } else {
868
+ this._destroyPlayback(layer.current);
869
+ }
870
+ }
871
+ this._layers.length = 0;
872
+ }
873
+
874
+ private _forEachAction(
875
+ motion: RuntimeMotion<TAction>,
876
+ visitor: (action: TAction) => void,
877
+ ): void {
878
+ if (motion.kind === 'clip') {
879
+ visitor(motion.action);
880
+ return;
881
+ }
882
+ for (const child of motion.children) this._forEachAction(child, visitor);
883
+ }
884
+
885
+ private _snapshotLayer(
886
+ layer: LayerRuntime<TAction>,
887
+ ): Animation3DStateMachineLayerSnapshot {
888
+ const active = layer.transition;
889
+ return Object.freeze({
890
+ layerId: layer.definition.id,
891
+ currentStateId: active?.source.state.id ?? layer.current.state.id,
892
+ currentTime: active?.source.time ?? layer.current.time,
893
+ transitionId: active?.definition.id ?? null,
894
+ sourceStateId: active?.source.state.id ?? null,
895
+ destinationStateId: active?.destination.state.id ?? null,
896
+ transitionProgress: active
897
+ ? (
898
+ active.definition.duration <= TIME_EPSILON
899
+ ? 1
900
+ : Math.max(0, Math.min(1, active.elapsed / active.definition.duration))
901
+ )
902
+ : 0,
903
+ });
904
+ }
905
+
906
+ private _setTypedParameter(
907
+ name: string,
908
+ expectedType: CompiledAnimation3DParameter['type'],
909
+ value: number,
910
+ ): void {
911
+ this._requireActive();
912
+ const parameter = this._resolveParameter(name);
913
+ if (parameter.type !== expectedType) {
914
+ throw new TypeError(
915
+ `Parameter "${name}" is ${parameter.type}, not ${expectedType}.`,
916
+ );
917
+ }
918
+ this._parameterValues[parameter.index] = value;
919
+ }
920
+
921
+ private _resolveParameter(name: string): CompiledAnimation3DParameter {
922
+ const parameterIndex = this.compiled.parameterIndexByName.get(name);
923
+ if (parameterIndex === undefined) throw new ReferenceError(`Unknown parameter "${name}".`);
924
+ return this.compiled.parameters[parameterIndex]!;
925
+ }
926
+
927
+ private _restoreParameterDefaults(): void {
928
+ for (const parameter of this.compiled.parameters) {
929
+ this._parameterValues[parameter.index] = parameter.defaultValue;
930
+ }
931
+ }
932
+
933
+ private _requireActive(): void {
934
+ if (this._status === 'destroyed') {
935
+ throw new Error('Animation3D state-machine controller is destroyed.');
936
+ }
937
+ }
938
+ }
939
+
940
+ /**
941
+ * @internal Extends one controller update transaction through caller-owned
942
+ * pose evaluation. It is intentionally not re-exported from package entries.
943
+ */
944
+ export function runAnimationStateMachineControllerUpdateTransaction<
945
+ TAction extends Animation3DStateMachineActionHandle,
946
+ TResult,
947
+ >(
948
+ controller: Animation3DStateMachineController<TAction>,
949
+ deltaSeconds: number,
950
+ beforeUpdate: () => void,
951
+ complete: () => TResult,
952
+ ): TResult {
953
+ return controller[CONTROLLER_UPDATE_TRANSACTION](
954
+ deltaSeconds,
955
+ beforeUpdate,
956
+ complete,
957
+ ) as TResult;
958
+ }
959
+
960
+ function resolveTransactionalPort(
961
+ port: Animation3DStateMachineMixerPort<Animation3DStateMachineActionHandle>,
962
+ ): TransactionalAnimationStateMachineMixerPort | null {
963
+ const candidate = port as Animation3DStateMachineMixerPort<Animation3DStateMachineActionHandle>
964
+ & Partial<TransactionalAnimationStateMachineMixerPort>;
965
+ const hasBegin = typeof candidate.beginControllerTransaction === 'function';
966
+ const hasCommit = typeof candidate.commitControllerTransaction === 'function';
967
+ const hasRollback = typeof candidate.rollbackControllerTransaction === 'function';
968
+ const implemented = Number(hasBegin) + Number(hasCommit) + Number(hasRollback);
969
+ if (implemented === 0) return null;
970
+ if (implemented !== 3) {
971
+ throw new TypeError(
972
+ 'A transactional state-machine mixer port must implement begin, commit, and rollback.',
973
+ );
974
+ }
975
+ return candidate as TransactionalAnimationStateMachineMixerPort;
976
+ }
977
+
978
+ /** Factory kept internal to the runtime directory. */
979
+ export function createAnimation3DStateMachineController<
980
+ TAction extends Animation3DStateMachineActionHandle,
981
+ >(
982
+ compiled: CompiledAnimation3DStateMachine,
983
+ port: Animation3DStateMachineMixerPort<TAction>,
984
+ options?: Animation3DStateMachineControllerOptions,
985
+ ): Animation3DStateMachineController<TAction> {
986
+ return new Animation3DStateMachineController(compiled, port, options);
987
+ }