@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,1708 @@
1
+ import { A as Animation3DError, b as animation3DMixerRuntime, d as Animation3DPoseBuffer, e as Animation3DMixer, G as GltfAnimation3DRuntime } from './chunks/GltfAnimation3DAdapter-Cu3rQQsW.js';
2
+ import { A as Animation3DStateMachineController$1, r as runAnimationStateMachineControllerUpdateTransaction, b as compileAnimation3DStateMachineDefinition$1, d as validateAnimation3DStateMachineDefinition$1, c as compileAnimationStateMachineDefinition } from './chunks/AnimationStateMachineController-BIcAh-sT.js';
3
+ import { Entity, Mesh3D, Component, Camera3D } from '@haiyue/engine';
4
+ import { MeshHelper, Transform3D, ParticleEmitter3D } from '@haiyue/engine/components';
5
+ import { createCone3D, createCylinder3D, createPlane3D, createSphere3D, createBox3D } from '@haiyue/engine/geometry';
6
+ import { AmbientLight, DirectionalLight } from '@haiyue/engine/lighting';
7
+ import { PbrMaterial } from '@haiyue/engine/material';
8
+ import { d as disposeGltfModel, a as loadGltfModel } from './chunks/gltfLoader-Dm22jLYB.js';
9
+ import { c as composeTrsMatrix } from './chunks/GltfAnimationRuntime-CRHsLycN.js';
10
+ import '@haiyue/engine/experimental/async';
11
+ import 'wgpu-matrix';
12
+ import '@haiyue/engine/core';
13
+
14
+ const POSE_APPLIER_STATES = new WeakMap();
15
+ /**
16
+ * Applies pose channels through a revision-aware binding resolver cache.
17
+ * Resolver misses fail explicitly and never write to a fallback target.
18
+ */
19
+ class Animation3DPoseApplier {
20
+ constructor(resolver) {
21
+ POSE_APPLIER_STATES.set(this, {
22
+ resolver,
23
+ resolverRevision: Number.NaN,
24
+ resolvedBindings: new Map(),
25
+ });
26
+ }
27
+ apply(pose) {
28
+ const state = POSE_APPLIER_STATES.get(this);
29
+ const { resolver } = state;
30
+ if (state.resolverRevision !== resolver.revision) {
31
+ state.resolverRevision = resolver.revision;
32
+ state.resolvedBindings.clear();
33
+ }
34
+ for (let index = 0; index < pose.channels.length; index++) {
35
+ const channel = pose.channels[index];
36
+ const bindingId = channel.binding.id;
37
+ let resolved = state.resolvedBindings.get(bindingId);
38
+ if (resolved === undefined && !state.resolvedBindings.has(bindingId)) {
39
+ resolved = resolver.resolve(channel.binding);
40
+ state.resolvedBindings.set(bindingId, resolved);
41
+ }
42
+ if (!resolved) {
43
+ throw new Animation3DError('resolver-miss', `Animation3D binding resolver could not resolve "${bindingId}".`, { resolver: 'binding', bindingId });
44
+ }
45
+ resolved.write(channel.value);
46
+ }
47
+ }
48
+ }
49
+
50
+ let nextAdapterId = 1;
51
+ class Animation3DStateMachineActionRuntimeHandle {
52
+ duration;
53
+ clipId;
54
+ layerId;
55
+ stateId;
56
+ action;
57
+ targetTime = 0;
58
+ appliedTime = 0;
59
+ playing = false;
60
+ destroyed = false;
61
+ pendingDestroy = false;
62
+ _actionCheckpoint;
63
+ _checkpointTargetTime = 0;
64
+ _checkpointAppliedTime = 0;
65
+ _checkpointPlaying = false;
66
+ constructor(clipId, options, action) {
67
+ this.duration = action.clip.duration;
68
+ this.clipId = clipId;
69
+ this.layerId = options.layerId;
70
+ this.stateId = options.stateId;
71
+ this.action = action;
72
+ this._actionCheckpoint = action.createSynchronizedState();
73
+ }
74
+ captureTransactionState() {
75
+ this._checkpointTargetTime = this.targetTime;
76
+ this._checkpointAppliedTime = this.appliedTime;
77
+ this._checkpointPlaying = this.playing;
78
+ this.action.captureSynchronizedState(this._actionCheckpoint);
79
+ }
80
+ restoreTransactionState() {
81
+ this.targetTime = this._checkpointTargetTime;
82
+ this.appliedTime = this._checkpointAppliedTime;
83
+ this.playing = this._checkpointPlaying;
84
+ this.pendingDestroy = false;
85
+ this.action.restoreSynchronizedState(this._actionCheckpoint);
86
+ }
87
+ }
88
+ /**
89
+ * Adapts the dimension-neutral state-machine port to the real 3D mixer.
90
+ *
91
+ * The controller owns every action playhead. The adapter applies its absolute
92
+ * times to actions while a synchronized mixer frame is open; it never calls
93
+ * mixer.update(), so the mixer clock cannot advance the same actions again.
94
+ */
95
+ class Animation3DStateMachineMixerAdapter {
96
+ mixer;
97
+ clipResolver;
98
+ _handles = new Set();
99
+ _transactionHandles = [];
100
+ _transactionCreated = [];
101
+ _transactionPendingDestroy = [];
102
+ _actionIdPrefix;
103
+ _nextActionId = 1;
104
+ _transactionNextActionId = 1;
105
+ _frameOut = null;
106
+ _controllerTransactionActive = false;
107
+ _destroyed = false;
108
+ constructor(mixer, clipResolver) {
109
+ this.mixer = mixer;
110
+ this.clipResolver = clipResolver;
111
+ this._actionIdPrefix = `animation3d-state-machine:${nextAdapterId++}`;
112
+ }
113
+ get liveActionCount() {
114
+ return this._handles.size;
115
+ }
116
+ createAction(clipId, options) {
117
+ this._requireActive();
118
+ const clip = this.clipResolver.resolve(clipId);
119
+ if (!clip) {
120
+ throw new Animation3DError('resolver-miss', `Animation3D clip resolver could not resolve "${clipId}".`, { resolver: 'clip', clipId });
121
+ }
122
+ const action = this.mixer.createAction(clip, {
123
+ id: `${this._actionIdPrefix}:${this._nextActionId++}`,
124
+ loop: options.loop,
125
+ repetitions: options.loop === 'once' ? 1 : Infinity,
126
+ clampWhenFinished: true,
127
+ weight: 0,
128
+ blendMode: options.blendMode,
129
+ ...(options.mask ? { mask: options.mask } : {}),
130
+ });
131
+ const handle = new Animation3DStateMachineActionRuntimeHandle(clipId, options, action);
132
+ this._handles.add(handle);
133
+ if (this._controllerTransactionActive)
134
+ this._transactionCreated.push(handle);
135
+ return handle;
136
+ }
137
+ play(action) {
138
+ this._requireHandle(action);
139
+ action.action.play();
140
+ action.playing = true;
141
+ }
142
+ stop(action) {
143
+ this._requireHandle(action);
144
+ action.action.stop();
145
+ action.playing = false;
146
+ }
147
+ fade(action, _targetWeight, _durationSeconds) {
148
+ this._requireHandle(action);
149
+ // The controller computes exact source/destination weights for every
150
+ // transition segment. Scheduling an action-local fade here would multiply
151
+ // that weight and introduce a second transition clock.
152
+ }
153
+ setWeight(action, weight) {
154
+ this._requireHandle(action);
155
+ action.action.weight = weight;
156
+ }
157
+ setTime(action, timeSeconds) {
158
+ this._requireHandle(action);
159
+ action.targetTime = timeSeconds;
160
+ if (!action.playing) {
161
+ action.action.seekSynchronizedTime(timeSeconds);
162
+ action.appliedTime = timeSeconds;
163
+ return;
164
+ }
165
+ if (this._frameOut)
166
+ this._applyTime(action, this._frameOut);
167
+ }
168
+ setTimeScale(action, timeScale) {
169
+ this._requireHandle(action);
170
+ action.action.timeScale = timeScale;
171
+ }
172
+ destroyAction(action) {
173
+ if (action.destroyed)
174
+ return;
175
+ if (!this._handles.has(action)) {
176
+ throw new Error('Animation3D state-machine action belongs to another adapter.');
177
+ }
178
+ if (this._controllerTransactionActive) {
179
+ if (action.pendingDestroy)
180
+ return;
181
+ action.pendingDestroy = true;
182
+ this._transactionPendingDestroy.push(action);
183
+ return;
184
+ }
185
+ this._destroyHandleNow(action);
186
+ }
187
+ /** @internal Called by the shared controller transaction boundary. */
188
+ beginControllerTransaction() {
189
+ this._requireActive();
190
+ if (this._controllerTransactionActive) {
191
+ throw new Error('Animation3D state-machine adapter transaction is already active.');
192
+ }
193
+ this._controllerTransactionActive = true;
194
+ this._transactionNextActionId = this._nextActionId;
195
+ this._transactionHandles.length = 0;
196
+ this._transactionCreated.length = 0;
197
+ this._transactionPendingDestroy.length = 0;
198
+ for (const handle of this._handles) {
199
+ handle.captureTransactionState();
200
+ this._transactionHandles.push(handle);
201
+ }
202
+ }
203
+ /** @internal Makes deferred action destruction observable atomically. */
204
+ commitControllerTransaction() {
205
+ if (!this._controllerTransactionActive) {
206
+ throw new Error('Animation3D state-machine adapter has no active transaction.');
207
+ }
208
+ this._controllerTransactionActive = false;
209
+ for (let index = 0; index < this._transactionPendingDestroy.length; index++) {
210
+ this._destroyHandleNow(this._transactionPendingDestroy[index]);
211
+ }
212
+ this._clearControllerTransaction();
213
+ }
214
+ /** @internal Removes new actions and restores exact event/playhead state. */
215
+ rollbackControllerTransaction() {
216
+ if (!this._controllerTransactionActive)
217
+ return;
218
+ this._controllerTransactionActive = false;
219
+ for (let index = this._transactionCreated.length - 1; index >= 0; index--) {
220
+ this._destroyHandleNow(this._transactionCreated[index]);
221
+ }
222
+ for (let index = 0; index < this._transactionHandles.length; index++) {
223
+ const handle = this._transactionHandles[index];
224
+ if (!handle.destroyed)
225
+ handle.restoreTransactionState();
226
+ }
227
+ this._nextActionId = this._transactionNextActionId;
228
+ this._clearControllerTransaction();
229
+ }
230
+ beginFrame(out) {
231
+ this._requireActive();
232
+ if (this._frameOut) {
233
+ throw new Error('Animation3D state-machine adapter already has an active frame.');
234
+ }
235
+ this._frameOut = out;
236
+ for (const handle of this._handles) {
237
+ if (handle.playing)
238
+ this._applyTime(handle, out);
239
+ }
240
+ }
241
+ endFrame(out) {
242
+ if (this._frameOut !== out) {
243
+ throw new Error('Animation3D state-machine adapter output does not match its active frame.');
244
+ }
245
+ this._frameOut = null;
246
+ }
247
+ cancelFrame(out) {
248
+ if (this._frameOut === out)
249
+ this._frameOut = null;
250
+ }
251
+ /**
252
+ * Releases adapter-created actions only. The mixer, clips, and clip resolver
253
+ * are externally owned and are never destroyed by this adapter.
254
+ */
255
+ destroy() {
256
+ if (this._destroyed)
257
+ return;
258
+ this.rollbackControllerTransaction();
259
+ this._frameOut = null;
260
+ for (const handle of this._handles)
261
+ this._destroyHandleNow(handle);
262
+ this._destroyed = true;
263
+ }
264
+ _applyTime(handle, out) {
265
+ handle.action.synchronizeTime(handle.targetTime, out);
266
+ handle.appliedTime = handle.targetTime;
267
+ }
268
+ _destroyHandleNow(action) {
269
+ if (action.destroyed)
270
+ return;
271
+ if (action.playing)
272
+ action.action.stop();
273
+ this.mixer.removeAction(action.action);
274
+ action.playing = false;
275
+ action.pendingDestroy = false;
276
+ action.destroyed = true;
277
+ this._handles.delete(action);
278
+ }
279
+ _clearControllerTransaction() {
280
+ for (let index = 0; index < this._transactionPendingDestroy.length; index++) {
281
+ this._transactionPendingDestroy[index].pendingDestroy = false;
282
+ }
283
+ this._transactionHandles.length = 0;
284
+ this._transactionCreated.length = 0;
285
+ this._transactionPendingDestroy.length = 0;
286
+ }
287
+ _requireActive() {
288
+ if (this._destroyed) {
289
+ throw new Error('Animation3D state-machine mixer adapter has been destroyed.');
290
+ }
291
+ }
292
+ _requireHandle(handle) {
293
+ this._requireActive();
294
+ if (handle.destroyed || !this._handles.has(handle)) {
295
+ throw new Error('Animation3D state-machine action is no longer valid.');
296
+ }
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Closed-loop runtime for a compiled state machine and the real 3D mixer.
302
+ *
303
+ * The state-machine controller is the unique playback-clock owner. A frame
304
+ * advances the controller once, synchronizes absolute action playheads, then
305
+ * asks the mixer to evaluate without advancing any action time.
306
+ */
307
+ class Animation3DStateMachineMixerIntegration {
308
+ mixer;
309
+ adapter;
310
+ controller;
311
+ _state = 'active';
312
+ _time = 0;
313
+ _transactionOut = null;
314
+ _beginControllerUpdate = () => {
315
+ this.adapter.beginFrame(this._requireTransactionOut());
316
+ };
317
+ _completeControllerUpdate = () => {
318
+ const out = this._requireTransactionOut();
319
+ this.adapter.endFrame(out);
320
+ return this.mixer.endSynchronizedFrame(out);
321
+ };
322
+ constructor(compiled, mixer, clipResolver, controllerOptions = {}) {
323
+ this.mixer = mixer;
324
+ this.adapter = new Animation3DStateMachineMixerAdapter(mixer, clipResolver);
325
+ try {
326
+ this.controller = new Animation3DStateMachineController$1(compiled, this.adapter, controllerOptions);
327
+ }
328
+ catch (error) {
329
+ this.adapter.destroy();
330
+ this._state = 'destroyed';
331
+ throw error;
332
+ }
333
+ }
334
+ get state() {
335
+ return this._state;
336
+ }
337
+ get time() {
338
+ return this._time;
339
+ }
340
+ update(deltaSeconds, out) {
341
+ this._requireActive();
342
+ if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) {
343
+ throw new RangeError('Animation3D state-machine deltaSeconds must be finite and non-negative.');
344
+ }
345
+ const nextTime = this._time + deltaSeconds;
346
+ this.mixer.beginSynchronizedFrame(nextTime, out);
347
+ this._transactionOut = out;
348
+ try {
349
+ const pose = runAnimationStateMachineControllerUpdateTransaction(this.controller, deltaSeconds, this._beginControllerUpdate, this._completeControllerUpdate);
350
+ this._time = nextTime;
351
+ return pose;
352
+ }
353
+ catch (error) {
354
+ this.adapter.cancelFrame(out);
355
+ this.mixer.cancelSynchronizedFrame(out);
356
+ throw error;
357
+ }
358
+ finally {
359
+ this._transactionOut = null;
360
+ }
361
+ }
362
+ /**
363
+ * Re-evaluates the current controller-owned time without advancing it.
364
+ */
365
+ evaluate(out) {
366
+ this._requireActive();
367
+ this.mixer.beginSynchronizedFrame(this._time, out);
368
+ this.adapter.beginControllerTransaction();
369
+ try {
370
+ this.adapter.beginFrame(out);
371
+ this.adapter.endFrame(out);
372
+ const pose = this.mixer.endSynchronizedFrame(out);
373
+ this.adapter.commitControllerTransaction();
374
+ return pose;
375
+ }
376
+ catch (error) {
377
+ this.adapter.cancelFrame(out);
378
+ this.mixer.cancelSynchronizedFrame(out);
379
+ this.adapter.rollbackControllerTransaction();
380
+ throw error;
381
+ }
382
+ }
383
+ reset(out) {
384
+ this._requireActive();
385
+ this.controller.reset();
386
+ this._time = 0;
387
+ return this.evaluate(out);
388
+ }
389
+ /**
390
+ * Stops and removes every controller action. External mixer/clip resources
391
+ * remain caller-owned. Repeated calls are safe.
392
+ */
393
+ destroy() {
394
+ if (this._state === 'destroyed')
395
+ return;
396
+ this.controller.destroy();
397
+ this.adapter.destroy();
398
+ this._state = 'destroyed';
399
+ }
400
+ _requireActive() {
401
+ if (this._state !== 'active') {
402
+ throw new Error('Animation3D state-machine mixer integration has been destroyed.');
403
+ }
404
+ }
405
+ _requireTransactionOut() {
406
+ if (!this._transactionOut) {
407
+ throw new Error('Animation3D state-machine integration has no active transaction output.');
408
+ }
409
+ return this._transactionOut;
410
+ }
411
+ }
412
+
413
+ class Animation3DStateMachineValidationError extends Error {
414
+ issues;
415
+ constructor(issues) {
416
+ super(`Invalid Animation3D state-machine definition (${issues.length} ${issues.length === 1 ? 'issue' : 'issues'}).`);
417
+ this.name = 'Animation3DStateMachineValidationError';
418
+ this.issues = Object.freeze([...issues]);
419
+ }
420
+ }
421
+ function validateAnimation3DStateMachineDefinition(definition) {
422
+ return validateAnimation3DStateMachineDefinition$1(definition);
423
+ }
424
+ function compileAnimation3DStateMachineDefinition(definition) {
425
+ const issues = validateAnimation3DStateMachineDefinition(definition);
426
+ if (issues.length > 0) {
427
+ throw new Animation3DStateMachineValidationError(issues);
428
+ }
429
+ return compileAnimation3DStateMachineDefinition$1(definition);
430
+ }
431
+ /**
432
+ * Public closed-loop controller and minimal Mixer integration.
433
+ *
434
+ * The internal mixer port, action handles, controller transactions and
435
+ * compiled nodes never cross this boundary.
436
+ */
437
+ class Animation3DStateMachineController {
438
+ mixer;
439
+ #integration;
440
+ constructor(compiled, mixer, clipResolver, options = {}) {
441
+ this.mixer = mixer;
442
+ this.#integration = new Animation3DStateMachineMixerIntegration(compiled, animation3DMixerRuntime(mixer), clipResolver, options);
443
+ }
444
+ get status() {
445
+ return this.#integration.state;
446
+ }
447
+ get time() {
448
+ return this.#integration.time;
449
+ }
450
+ get layerSnapshots() {
451
+ return this.#integration.controller.layerSnapshots;
452
+ }
453
+ getLayerSnapshot(layerId) {
454
+ return this.#integration.controller.getLayerSnapshot(layerId);
455
+ }
456
+ setFloat(name, value) {
457
+ this.#integration.controller.setFloat(name, value);
458
+ return this;
459
+ }
460
+ setInteger(name, value) {
461
+ this.#integration.controller.setInteger(name, value);
462
+ return this;
463
+ }
464
+ setBoolean(name, value) {
465
+ this.#integration.controller.setBoolean(name, value);
466
+ return this;
467
+ }
468
+ setTrigger(name) {
469
+ this.#integration.controller.setTrigger(name);
470
+ return this;
471
+ }
472
+ resetTrigger(name) {
473
+ this.#integration.controller.resetTrigger(name);
474
+ return this;
475
+ }
476
+ getParameter(name) {
477
+ return this.#integration.controller.getParameter(name);
478
+ }
479
+ update(deltaSeconds, out) {
480
+ return this.#integration.update(deltaSeconds, out);
481
+ }
482
+ evaluate(out) {
483
+ return this.#integration.evaluate(out);
484
+ }
485
+ reset(out) {
486
+ return this.#integration.reset(out);
487
+ }
488
+ destroy() {
489
+ this.#integration.destroy();
490
+ }
491
+ }
492
+
493
+ const TIME_EPSILON = 1e-9;
494
+ class HyaAnimation3DStateMachineActionHandle {
495
+ id;
496
+ clipId;
497
+ duration;
498
+ loop;
499
+ children;
500
+ activeCueKeys = new Set();
501
+ targetTime = 0;
502
+ traversal = 0;
503
+ playing = false;
504
+ destroyed = false;
505
+ pendingDestroy = false;
506
+ constructor(id, clipId, duration, loop, children) {
507
+ this.id = id;
508
+ this.clipId = clipId;
509
+ this.duration = duration;
510
+ this.loop = loop;
511
+ this.children = Object.freeze([...children]);
512
+ }
513
+ }
514
+ /**
515
+ * One controller-facing port spanning the scene mixer and every loaded model
516
+ * mixer. It is the only state-machine clock; child adapters only synchronize
517
+ * existing mixer actions and participate in the same rollback boundary.
518
+ */
519
+ class HyaAnimation3DStateMachineMixerPort {
520
+ partitions;
521
+ _particleCues;
522
+ _clips = new Map();
523
+ _particles = new Map();
524
+ _owners = new Map();
525
+ _handles = new Set();
526
+ _transactionCreated = [];
527
+ _transactionPendingDestroy = [];
528
+ _transactionCheckpoints = new Map();
529
+ _transactionCommands = [];
530
+ _transactionOwners = new Map();
531
+ _nextHandleId = 1;
532
+ _transactionNextHandleId = 1;
533
+ _transactionActive = false;
534
+ _destroyed = false;
535
+ constructor(partitions, clips, _particleCues) {
536
+ this.partitions = partitions;
537
+ this._particleCues = _particleCues;
538
+ for (const clip of clips)
539
+ this._clips.set(clip.id, clip);
540
+ for (const cues of _particleCues.values()) {
541
+ for (const cue of cues)
542
+ this._particles.set(cue.key, cue.emitter);
543
+ }
544
+ }
545
+ get liveActionCount() { return this._handles.size; }
546
+ get sideEffectOwnerCount() {
547
+ let count = 0;
548
+ for (const owners of this._owners.values())
549
+ count += owners.length;
550
+ return count;
551
+ }
552
+ createAction(clipId, options) {
553
+ this._requireActive();
554
+ const clip = this._clips.get(clipId);
555
+ if (!clip)
556
+ throw new RangeError(`Unknown HYA Animation3D state-machine clip "${clipId}".`);
557
+ const children = [];
558
+ try {
559
+ for (const partition of this.partitions) {
560
+ if (partition.clips.has(clipId)) {
561
+ children.push(partition.adapter.createAction(clipId, translateActionOptions(options, partition.bindingIds)));
562
+ }
563
+ }
564
+ }
565
+ catch (error) {
566
+ let childIndex = 0;
567
+ for (const partition of this.partitions) {
568
+ if (!partition.clips.has(clipId) || childIndex >= children.length)
569
+ continue;
570
+ partition.adapter.destroyAction(children[childIndex++]);
571
+ }
572
+ throw error;
573
+ }
574
+ const handle = new HyaAnimation3DStateMachineActionHandle(`hya-animation3d-state-machine:${this._nextHandleId++}`, clipId, clip.duration, options.loop, children);
575
+ this._handles.add(handle);
576
+ if (this._transactionActive)
577
+ this._transactionCreated.push(handle);
578
+ return handle;
579
+ }
580
+ play(handle) {
581
+ this._requireHandle(handle);
582
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.play(child));
583
+ if (handle.playing)
584
+ return;
585
+ handle.playing = true;
586
+ this._syncParticleCues(handle, true);
587
+ }
588
+ stop(handle) {
589
+ this._requireHandle(handle);
590
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.stop(child));
591
+ if (!handle.playing)
592
+ return;
593
+ handle.playing = false;
594
+ this._releaseAllParticleCues(handle);
595
+ }
596
+ fade(handle, targetWeight, durationSeconds) {
597
+ this._requireHandle(handle);
598
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.fade(child, targetWeight, durationSeconds));
599
+ }
600
+ setWeight(handle, weight) {
601
+ this._requireHandle(handle);
602
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.setWeight(child, weight));
603
+ }
604
+ setTime(handle, timeSeconds) {
605
+ this._requireHandle(handle);
606
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.setTime(child, timeSeconds));
607
+ const previousTraversal = handle.traversal;
608
+ const position = playbackPosition(timeSeconds, handle.duration, handle.loop);
609
+ handle.targetTime = timeSeconds;
610
+ handle.traversal = position.traversal;
611
+ if (!handle.playing)
612
+ return;
613
+ this._syncParticleCues(handle, false, position.localTime);
614
+ if (position.traversal !== previousTraversal)
615
+ this._restartDominantParticleCues(handle);
616
+ }
617
+ setTimeScale(handle, timeScale) {
618
+ this._requireHandle(handle);
619
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.setTimeScale(child, timeScale));
620
+ }
621
+ destroyAction(handle) {
622
+ if (handle.destroyed)
623
+ return;
624
+ this._requireHandle(handle);
625
+ if (handle.playing) {
626
+ handle.playing = false;
627
+ this._releaseAllParticleCues(handle);
628
+ }
629
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.destroyAction(child));
630
+ if (this._transactionActive) {
631
+ if (!handle.pendingDestroy) {
632
+ handle.pendingDestroy = true;
633
+ this._transactionPendingDestroy.push(handle);
634
+ }
635
+ return;
636
+ }
637
+ this._destroyGroupNow(handle);
638
+ }
639
+ beginControllerTransaction() {
640
+ this._requireActive();
641
+ if (this._transactionActive)
642
+ throw new Error('HYA Animation3D state-machine transaction is already active.');
643
+ this._transactionActive = true;
644
+ this._transactionCreated.length = 0;
645
+ this._transactionPendingDestroy.length = 0;
646
+ this._transactionCheckpoints.clear();
647
+ this._transactionCommands.length = 0;
648
+ this._transactionOwners = cloneOwners(this._owners);
649
+ this._transactionNextHandleId = this._nextHandleId;
650
+ for (const handle of this._handles) {
651
+ this._transactionCheckpoints.set(handle, {
652
+ targetTime: handle.targetTime,
653
+ traversal: handle.traversal,
654
+ playing: handle.playing,
655
+ activeCueKeys: new Set(handle.activeCueKeys),
656
+ });
657
+ }
658
+ const begun = [];
659
+ try {
660
+ for (const partition of this.partitions) {
661
+ partition.adapter.beginControllerTransaction();
662
+ begun.push(partition.adapter);
663
+ }
664
+ }
665
+ catch (error) {
666
+ for (let index = begun.length - 1; index >= 0; index--)
667
+ begun[index].rollbackControllerTransaction();
668
+ this._transactionActive = false;
669
+ this._clearTransaction();
670
+ throw error;
671
+ }
672
+ }
673
+ commitControllerTransaction() {
674
+ if (!this._transactionActive)
675
+ throw new Error('HYA Animation3D state-machine has no active transaction.');
676
+ for (const partition of this.partitions)
677
+ partition.adapter.commitControllerTransaction();
678
+ this._transactionActive = false;
679
+ for (const command of this._transactionCommands)
680
+ this._applyParticleCommand(command);
681
+ for (const handle of this._transactionPendingDestroy)
682
+ this._destroyGroupNow(handle);
683
+ this._clearTransaction();
684
+ }
685
+ rollbackControllerTransaction() {
686
+ if (!this._transactionActive)
687
+ return;
688
+ this._transactionActive = false;
689
+ for (let index = this.partitions.length - 1; index >= 0; index--) {
690
+ this.partitions[index].adapter.rollbackControllerTransaction();
691
+ }
692
+ for (const handle of this._transactionCreated) {
693
+ handle.destroyed = true;
694
+ this._handles.delete(handle);
695
+ }
696
+ for (const [handle, checkpoint] of this._transactionCheckpoints) {
697
+ if (handle.destroyed)
698
+ continue;
699
+ handle.targetTime = checkpoint.targetTime;
700
+ handle.traversal = checkpoint.traversal;
701
+ handle.playing = checkpoint.playing;
702
+ handle.pendingDestroy = false;
703
+ handle.activeCueKeys.clear();
704
+ for (const key of checkpoint.activeCueKeys)
705
+ handle.activeCueKeys.add(key);
706
+ }
707
+ this._owners.clear();
708
+ for (const [key, owners] of this._transactionOwners)
709
+ this._owners.set(key, [...owners]);
710
+ this._nextHandleId = this._transactionNextHandleId;
711
+ this._clearTransaction();
712
+ }
713
+ destroy() {
714
+ if (this._destroyed)
715
+ return;
716
+ this.rollbackControllerTransaction();
717
+ for (const handle of [...this._handles]) {
718
+ if (handle.playing)
719
+ this.stop(handle);
720
+ forEachChild(this.partitions, handle, (adapter, child) => adapter.destroyAction(child));
721
+ this._destroyGroupNow(handle);
722
+ }
723
+ for (const partition of this.partitions)
724
+ partition.adapter.destroy();
725
+ for (const emitter of this._particles.values())
726
+ stopParticle(emitter);
727
+ this._owners.clear();
728
+ this._destroyed = true;
729
+ }
730
+ _syncParticleCues(handle, entering, localTime = playbackPosition(handle.targetTime, handle.duration, handle.loop).localTime) {
731
+ for (const cue of this._particleCues.get(handle.clipId) ?? []) {
732
+ const active = particleCueActive(cue, localTime, handle.duration);
733
+ const owned = handle.activeCueKeys.has(cue.key);
734
+ if (active && !owned)
735
+ this._acquireParticleCue(handle, cue);
736
+ else if (!active && owned)
737
+ this._releaseParticleCue(handle, cue.key);
738
+ else if (entering && active && owned)
739
+ this._recordParticleCommand({ key: cue.key, kind: 'start' });
740
+ }
741
+ }
742
+ _acquireParticleCue(handle, cue) {
743
+ handle.activeCueKeys.add(cue.key);
744
+ const owners = this._owners.get(cue.key) ?? [];
745
+ if (!this._owners.has(cue.key))
746
+ this._owners.set(cue.key, owners);
747
+ const existing = owners.indexOf(handle.id);
748
+ if (existing >= 0)
749
+ owners.splice(existing, 1);
750
+ owners.push(handle.id);
751
+ this._recordParticleCommand({ key: cue.key, kind: 'start' });
752
+ }
753
+ _releaseParticleCue(handle, key) {
754
+ handle.activeCueKeys.delete(key);
755
+ const owners = this._owners.get(key);
756
+ if (!owners)
757
+ return;
758
+ const index = owners.indexOf(handle.id);
759
+ const wasDominant = index === owners.length - 1;
760
+ if (index >= 0)
761
+ owners.splice(index, 1);
762
+ if (owners.length === 0) {
763
+ this._owners.delete(key);
764
+ this._recordParticleCommand({ key, kind: 'stop' });
765
+ }
766
+ else if (wasDominant)
767
+ this._recordParticleCommand({ key, kind: 'start' });
768
+ }
769
+ _releaseAllParticleCues(handle) {
770
+ for (const key of [...handle.activeCueKeys])
771
+ this._releaseParticleCue(handle, key);
772
+ }
773
+ _restartDominantParticleCues(handle) {
774
+ for (const key of handle.activeCueKeys) {
775
+ const owners = this._owners.get(key);
776
+ if (owners?.[owners.length - 1] === handle.id)
777
+ this._recordParticleCommand({ key, kind: 'start' });
778
+ }
779
+ }
780
+ _recordParticleCommand(command) {
781
+ if (this._transactionActive)
782
+ this._transactionCommands.push(command);
783
+ else
784
+ this._applyParticleCommand(command);
785
+ }
786
+ _applyParticleCommand(command) {
787
+ const emitter = this._particles.get(command.key);
788
+ if (!emitter)
789
+ return;
790
+ if (command.kind === 'stop')
791
+ stopParticle(emitter);
792
+ else {
793
+ emitter.emitting = true;
794
+ emitter.restart(true);
795
+ emitter.playing = true;
796
+ }
797
+ }
798
+ _destroyGroupNow(handle) {
799
+ handle.playing = false;
800
+ handle.pendingDestroy = false;
801
+ handle.destroyed = true;
802
+ handle.activeCueKeys.clear();
803
+ this._handles.delete(handle);
804
+ }
805
+ _clearTransaction() {
806
+ this._transactionCreated.length = 0;
807
+ this._transactionPendingDestroy.length = 0;
808
+ this._transactionCheckpoints.clear();
809
+ this._transactionCommands.length = 0;
810
+ this._transactionOwners.clear();
811
+ }
812
+ _requireHandle(handle) {
813
+ this._requireActive();
814
+ if (handle.destroyed || !this._handles.has(handle)) {
815
+ throw new Error('HYA Animation3D state-machine action is no longer valid.');
816
+ }
817
+ }
818
+ _requireActive() {
819
+ if (this._destroyed)
820
+ throw new Error('HYA Animation3D state-machine mixer port has been destroyed.');
821
+ }
822
+ }
823
+ /** Coordinates native scene, glTF model and Particle3D channels with one controller transaction. */
824
+ class HyaAnimation3DStateMachineRuntime {
825
+ controller;
826
+ _partitions;
827
+ _port;
828
+ _time = 0;
829
+ _destroyed = false;
830
+ constructor(options) {
831
+ this._partitions = Object.freeze(options.partitions.map(partition => {
832
+ const resolver = { resolve: (clipId) => partition.clips.get(clipId) ?? null };
833
+ return {
834
+ id: partition.id,
835
+ mixer: partition.mixer,
836
+ adapter: new Animation3DStateMachineMixerAdapter(partition.mixer, resolver),
837
+ clips: partition.clips,
838
+ bindingIds: partition.bindingIds,
839
+ pose: partition.pose ?? new Animation3DPoseBuffer(),
840
+ apply: partition.apply,
841
+ previousTime: 0,
842
+ frameCompleted: false,
843
+ };
844
+ }));
845
+ this._port = new HyaAnimation3DStateMachineMixerPort(this._partitions, options.clips, options.particleCues ?? new Map());
846
+ try {
847
+ this.controller = new Animation3DStateMachineController$1(compileAnimationStateMachineDefinition(options.definition), this._port);
848
+ this.evaluate();
849
+ }
850
+ catch (error) {
851
+ this.controller?.destroy();
852
+ this._port.destroy();
853
+ this._destroyed = true;
854
+ throw error;
855
+ }
856
+ }
857
+ get time() { return this._time; }
858
+ get liveActionCount() { return this._port.liveActionCount; }
859
+ get liveBindingCount() {
860
+ let count = 0;
861
+ for (const partition of this._partitions)
862
+ count += partition.mixer.liveBindingCount;
863
+ return count;
864
+ }
865
+ get sideEffectOwnerCount() { return this._port.sideEffectOwnerCount; }
866
+ update(deltaSeconds) {
867
+ this._requireActive();
868
+ if (!Number.isFinite(deltaSeconds) || deltaSeconds < 0) {
869
+ throw new RangeError('HYA Animation3D state-machine deltaSeconds must be finite and non-negative.');
870
+ }
871
+ this._runFrame(deltaSeconds);
872
+ }
873
+ evaluate() {
874
+ this._requireActive();
875
+ this._runFrame(0);
876
+ }
877
+ reset() {
878
+ this._requireActive();
879
+ this.controller.reset();
880
+ this._time = 0;
881
+ this.evaluate();
882
+ }
883
+ destroy() {
884
+ if (this._destroyed)
885
+ return;
886
+ this.controller.destroy();
887
+ this._port.destroy();
888
+ this._destroyed = true;
889
+ }
890
+ _runFrame(deltaSeconds) {
891
+ const nextTime = this._time + deltaSeconds;
892
+ this._openFrames(nextTime);
893
+ let poses;
894
+ try {
895
+ poses = runAnimationStateMachineControllerUpdateTransaction(this.controller, deltaSeconds, () => {
896
+ for (const partition of this._partitions)
897
+ partition.adapter.beginFrame(partition.pose);
898
+ }, () => {
899
+ for (const partition of this._partitions)
900
+ partition.adapter.endFrame(partition.pose);
901
+ const result = [];
902
+ for (const partition of this._partitions) {
903
+ result.push(partition.mixer.endSynchronizedFrame(partition.pose));
904
+ partition.frameCompleted = true;
905
+ }
906
+ return result;
907
+ });
908
+ }
909
+ catch (error) {
910
+ this._cancelFrames();
911
+ throw error;
912
+ }
913
+ for (const partition of this._partitions)
914
+ partition.frameCompleted = false;
915
+ this._time = nextTime;
916
+ for (let index = 0; index < this._partitions.length; index++) {
917
+ this._partitions[index].apply(poses[index]);
918
+ }
919
+ }
920
+ _openFrames(time) {
921
+ const opened = [];
922
+ try {
923
+ for (const partition of this._partitions) {
924
+ partition.previousTime = partition.mixer.time;
925
+ partition.frameCompleted = false;
926
+ partition.mixer.beginSynchronizedFrame(time, partition.pose);
927
+ opened.push(partition);
928
+ }
929
+ }
930
+ catch (error) {
931
+ for (let index = opened.length - 1; index >= 0; index--) {
932
+ opened[index].mixer.cancelSynchronizedFrame(opened[index].pose);
933
+ }
934
+ throw error;
935
+ }
936
+ }
937
+ _cancelFrames() {
938
+ for (const partition of this._partitions)
939
+ partition.adapter.cancelFrame(partition.pose);
940
+ for (let index = this._partitions.length - 1; index >= 0; index--) {
941
+ const partition = this._partitions[index];
942
+ if (partition.frameCompleted) {
943
+ partition.mixer.rollbackCompletedSynchronizedFrame(partition.previousTime);
944
+ partition.frameCompleted = false;
945
+ }
946
+ else
947
+ partition.mixer.cancelSynchronizedFrame(partition.pose);
948
+ }
949
+ }
950
+ _requireActive() {
951
+ if (this._destroyed)
952
+ throw new Error('HYA Animation3D state-machine runtime has been destroyed.');
953
+ }
954
+ }
955
+ function forEachChild(partitions, handle, visit) {
956
+ let childIndex = 0;
957
+ for (const partition of partitions) {
958
+ if (!partition.clips.has(handle.clipId))
959
+ continue;
960
+ visit(partition.adapter, handle.children[childIndex++]);
961
+ }
962
+ }
963
+ function cloneOwners(source) {
964
+ const result = new Map();
965
+ for (const [key, owners] of source)
966
+ result.set(key, [...owners]);
967
+ return result;
968
+ }
969
+ function translateActionOptions(options, bindingIds) {
970
+ if (!options.mask || !bindingIds || bindingIds.size === 0)
971
+ return options;
972
+ const translate = (ids) => {
973
+ if (!ids)
974
+ return undefined;
975
+ return Object.freeze(ids.map(id => bindingIds.get(id) ?? id));
976
+ };
977
+ const include = translate(options.mask.include);
978
+ const exclude = translate(options.mask.exclude);
979
+ return {
980
+ ...options,
981
+ mask: Object.freeze({
982
+ ...(include === undefined ? {} : { include }),
983
+ ...(exclude === undefined ? {} : { exclude }),
984
+ }),
985
+ };
986
+ }
987
+ function playbackPosition(time, duration, loop) {
988
+ if (duration <= TIME_EPSILON || loop === 'once') {
989
+ return { localTime: Math.min(duration, Math.max(0, time)), traversal: 0 };
990
+ }
991
+ const traversal = Math.floor(time / duration);
992
+ let localTime = time - traversal * duration;
993
+ if (localTime < 0)
994
+ localTime += duration;
995
+ if (loop === 'ping-pong' && Math.abs(traversal % 2) === 1)
996
+ localTime = duration - localTime;
997
+ return { localTime, traversal };
998
+ }
999
+ function particleCueActive(cue, time, duration) {
1000
+ const atTerminalEnd = Math.abs(cue.end - duration) <= TIME_EPSILON
1001
+ && Math.abs(time - duration) <= TIME_EPSILON;
1002
+ return time >= cue.start - TIME_EPSILON
1003
+ && (time < cue.end - TIME_EPSILON || atTerminalEnd);
1004
+ }
1005
+ function stopParticle(emitter) {
1006
+ emitter.playing = false;
1007
+ emitter.emitting = false;
1008
+ emitter.clear();
1009
+ }
1010
+
1011
+ class HyaAnimation3DResolvedEndpoint {
1012
+ binding;
1013
+ _read;
1014
+ _write;
1015
+ constructor(binding, _read, _write) {
1016
+ this.binding = binding;
1017
+ this._read = _read;
1018
+ this._write = _write;
1019
+ }
1020
+ read(out) { this._read(out); }
1021
+ write(value) { this._write(value); }
1022
+ }
1023
+ class HyaAnimation3DBindingResolver {
1024
+ _endpoints = new Map();
1025
+ _revision = 0;
1026
+ _destroyed = false;
1027
+ get revision() { return this._revision; }
1028
+ get size() { return this._endpoints.size; }
1029
+ add(endpoint) {
1030
+ if (this._destroyed)
1031
+ throw new Error('HYA Animation3D resolver is destroyed.');
1032
+ this._endpoints.set(endpoint.binding.id, endpoint);
1033
+ this._revision++;
1034
+ }
1035
+ resolve(binding) {
1036
+ if (this._destroyed)
1037
+ return null;
1038
+ const endpoint = this._endpoints.get(binding.id);
1039
+ if (!endpoint || endpoint.binding.path !== binding.path
1040
+ || endpoint.binding.valueType !== binding.valueType
1041
+ || endpoint.binding.valueSize !== binding.valueSize)
1042
+ return null;
1043
+ return endpoint;
1044
+ }
1045
+ destroy() {
1046
+ if (this._destroyed)
1047
+ return;
1048
+ this._destroyed = true;
1049
+ this._endpoints.clear();
1050
+ this._revision++;
1051
+ }
1052
+ }
1053
+ class HyaAnimation3DRuntimeOwner extends Component {
1054
+ _runtime = null;
1055
+ constructor() { super('HyaAnimation3DRuntimeOwner'); }
1056
+ attach(runtime) { this._runtime = runtime; }
1057
+ release(runtime) { if (this._runtime === runtime)
1058
+ this._runtime = null; }
1059
+ clone() { return new HyaAnimation3DRuntimeOwner(); }
1060
+ destroy() {
1061
+ if (this.destroyed)
1062
+ return;
1063
+ const runtime = this._runtime;
1064
+ this._runtime = null;
1065
+ runtime?.destroy();
1066
+ super.destroy();
1067
+ }
1068
+ }
1069
+ class HyaAnimation3DActionGroupImpl {
1070
+ id;
1071
+ clipId;
1072
+ entries;
1073
+ constructor(id, clipId, entries) {
1074
+ this.id = id;
1075
+ this.clipId = clipId;
1076
+ this.entries = entries;
1077
+ }
1078
+ get actionCount() { return this.entries.length; }
1079
+ get playing() {
1080
+ return this.entries.some(entry => entry.owner.state === 'active'
1081
+ && (entry.action.status === 'running' || entry.action.status === 'scheduled'));
1082
+ }
1083
+ play() { for (const entry of this.entries)
1084
+ if (entry.owner.state === 'active')
1085
+ entry.action.play(); return this; }
1086
+ stop() { for (const entry of this.entries)
1087
+ if (entry.owner.state === 'active')
1088
+ entry.action.stop(); return this; }
1089
+ reset() { for (const entry of this.entries)
1090
+ if (entry.owner.state === 'active')
1091
+ entry.action.reset(); return this; }
1092
+ }
1093
+ /**
1094
+ * Optional native-3D HYA owner. The caller must pass an already validated
1095
+ * animation-spec payload; no ECS/GPU object is allocated by the parser.
1096
+ */
1097
+ class HyaAnimation3DRuntime {
1098
+ root = new Entity('HYA Native 3D');
1099
+ mixer;
1100
+ pose = new Animation3DPoseBuffer();
1101
+ _scene;
1102
+ _payload;
1103
+ _clips;
1104
+ _resources;
1105
+ _entities = new Map();
1106
+ _transforms = new Map();
1107
+ _materials = new Map();
1108
+ _cameras = new Map();
1109
+ _models = [];
1110
+ _mainClips = new Map();
1111
+ _modelClips = new Map();
1112
+ _modelBindingIds = new Map();
1113
+ _actionGroups = new Map();
1114
+ _resolver = new HyaAnimation3DBindingResolver();
1115
+ _poseApplier;
1116
+ _owner = new HyaAnimation3DRuntimeOwner();
1117
+ _geometries = new Set();
1118
+ _particles = [];
1119
+ _abortController = new AbortController();
1120
+ _externalSignal;
1121
+ _externalAbortListener;
1122
+ _disposeModel;
1123
+ _state = 'active';
1124
+ _initialized = false;
1125
+ _stateMachine = null;
1126
+ constructor(options) {
1127
+ this._scene = options.scene;
1128
+ this._payload = options.payload;
1129
+ this._clips = Object.freeze(options.payload.clips.map(lowerRuntimeClip));
1130
+ this._resources = new Map(options.resources.map(resource => [resource.id, resource]));
1131
+ this._disposeModel = options.disposeModel ?? disposeGltfModel;
1132
+ this.mixer = new Animation3DMixer(this._resolver);
1133
+ this._poseApplier = new Animation3DPoseApplier(this._resolver);
1134
+ this._owner.attach(this);
1135
+ this.root.addComponent(this._owner);
1136
+ this._externalSignal = options.signal ?? null;
1137
+ this._externalAbortListener = this._externalSignal ? () => this.destroy() : null;
1138
+ this._externalSignal?.addEventListener('abort', this._externalAbortListener, { once: true });
1139
+ }
1140
+ static async create(options) {
1141
+ if (options.signal?.aborted)
1142
+ throw abortError(options.signal.reason);
1143
+ const runtime = new HyaAnimation3DRuntime(options);
1144
+ try {
1145
+ await runtime._initialize(options);
1146
+ return runtime;
1147
+ }
1148
+ catch (error) {
1149
+ runtime.destroy();
1150
+ throw error;
1151
+ }
1152
+ }
1153
+ get state() { return this._state; }
1154
+ get initialized() { return this._initialized; }
1155
+ get clips() { return this._clips; }
1156
+ get authoredCameraIds() { return Object.freeze([...this._cameras.keys()]); }
1157
+ get stateMachine() { return this._stateMachine; }
1158
+ getEntity(nodeId) { return this._entities.get(nodeId) ?? null; }
1159
+ getMaterial(materialId) { return this._materials.get(materialId) ?? null; }
1160
+ getModelAnimationClips(nodeId) {
1161
+ return this._models.find(model => model.nodeId === nodeId)?.runtime.clips ?? Object.freeze([]);
1162
+ }
1163
+ select(nodeIds) {
1164
+ const selected = new Set(nodeIds);
1165
+ for (const [id, entity] of this._entities) {
1166
+ if (selected.has(id))
1167
+ entity.addComponent(new MeshHelper({ mode: 'aabb', color: [0.25, 0.75, 1, 1], lineWidth: 1.5 }));
1168
+ else
1169
+ entity.removeComponent(MeshHelper);
1170
+ }
1171
+ }
1172
+ useAuthoredCamera(nodeId) {
1173
+ this._assertActive();
1174
+ const entity = this._entities.get(nodeId);
1175
+ if (!entity || !this._cameras.has(nodeId))
1176
+ throw new RangeError(`Unknown authored camera node "${nodeId}".`);
1177
+ this._scene.setCamera(entity);
1178
+ }
1179
+ playClip(clipId, options = {}) {
1180
+ this._assertActive();
1181
+ if (this._stateMachine) {
1182
+ throw new Error('Manual HYA Animation3D clip actions are unavailable while the built-in state machine owns the mixers.');
1183
+ }
1184
+ const base = this._clips.find(clip => clip.id === clipId);
1185
+ if (!base)
1186
+ throw new RangeError(`Unknown HYA Animation3D clip "${clipId}".`);
1187
+ const groupId = options.id ?? `${clipId}:action:${this._actionGroups.size}`;
1188
+ if (this._actionGroups.has(groupId))
1189
+ throw new RangeError(`HYA Animation3D action group "${groupId}" already exists.`);
1190
+ const entries = [];
1191
+ const actionOptions = withoutGroupOptions(options);
1192
+ const main = this._mainClips.get(clipId);
1193
+ if (main)
1194
+ entries.push({ owner: this.mixer, action: this.mixer.createAction(main, { ...actionOptions, id: `${groupId}:scene` }) });
1195
+ for (const [model, clip] of this._modelClips.get(clipId) ?? []) {
1196
+ entries.push({ owner: model.runtime.mixer, action: model.runtime.mixer.createAction(clip, { ...actionOptions, id: `${groupId}:${model.componentId}` }) });
1197
+ }
1198
+ const group = new HyaAnimation3DActionGroupImpl(groupId, clipId, entries);
1199
+ if (options.fadeFrom) {
1200
+ const source = options.fadeFrom;
1201
+ const duration = options.fadeDuration ?? 0;
1202
+ for (const entry of entries) {
1203
+ const sourceEntry = source.entries.find(candidate => candidate.owner === entry.owner);
1204
+ if (sourceEntry)
1205
+ entry.action.crossFadeFrom(sourceEntry.action, duration, options.warp ?? false);
1206
+ else
1207
+ entry.action.fadeIn(duration).play();
1208
+ }
1209
+ }
1210
+ else
1211
+ group.play();
1212
+ this._actionGroups.set(groupId, group);
1213
+ return group;
1214
+ }
1215
+ removeActionGroup(group) {
1216
+ const id = typeof group === 'string' ? group : group.id;
1217
+ const target = this._actionGroups.get(id);
1218
+ if (!target)
1219
+ return false;
1220
+ for (const entry of target.entries) {
1221
+ if (entry.owner.state !== 'active')
1222
+ continue;
1223
+ entry.action.stop();
1224
+ entry.owner.removeAction(entry.action);
1225
+ }
1226
+ this._actionGroups.delete(id);
1227
+ return true;
1228
+ }
1229
+ update(deltaSeconds) {
1230
+ this._assertActive();
1231
+ if (this._stateMachine) {
1232
+ this._stateMachine.update(deltaSeconds);
1233
+ return;
1234
+ }
1235
+ this._poseApplier.apply(this.mixer.update(deltaSeconds, this.pose));
1236
+ for (const model of this._models)
1237
+ model.runtime.update(deltaSeconds);
1238
+ }
1239
+ setTime(timeSeconds) {
1240
+ this._assertActive();
1241
+ if (this._stateMachine) {
1242
+ throw new Error('HYA Animation3D setTime() is unavailable while the built-in state machine owns the playhead.');
1243
+ }
1244
+ this._poseApplier.apply(this.mixer.setTime(timeSeconds, this.pose));
1245
+ for (const model of this._models)
1246
+ model.runtime.setTime(timeSeconds);
1247
+ }
1248
+ diagnostics() {
1249
+ const residual = this._state === 'destroyed'
1250
+ ? this._entities.size + this._materials.size + this._models.length + this._actionGroups.size + this._resolver.size
1251
+ : 0;
1252
+ return Object.freeze({
1253
+ state: this._state,
1254
+ entityCount: this._entities.size,
1255
+ materialCount: this._materials.size,
1256
+ modelCount: this._models.length,
1257
+ actionGroupCount: this._actionGroups.size,
1258
+ ownerResidualCount: residual,
1259
+ });
1260
+ }
1261
+ destroy() {
1262
+ if (this._state === 'destroyed')
1263
+ return;
1264
+ this._state = 'destroyed';
1265
+ if (this._externalSignal && this._externalAbortListener)
1266
+ this._externalSignal.removeEventListener('abort', this._externalAbortListener);
1267
+ this._stateMachine?.destroy();
1268
+ this._stateMachine = null;
1269
+ for (const id of [...this._actionGroups.keys()])
1270
+ this.removeActionGroup(id);
1271
+ this._abortController.abort('hya-animation3d-runtime-destroyed');
1272
+ this.mixer.destroy();
1273
+ this._resolver.destroy();
1274
+ for (const model of this._models.splice(0)) {
1275
+ model.runtime.destroy();
1276
+ if (!model.model.root.destroyed)
1277
+ this._disposeModel(model.model);
1278
+ }
1279
+ this._owner.release(this);
1280
+ if (!this.root.destroyed)
1281
+ this.root.destroy();
1282
+ this._entities.clear();
1283
+ this._transforms.clear();
1284
+ this._materials.clear();
1285
+ this._cameras.clear();
1286
+ this._mainClips.clear();
1287
+ this._modelClips.clear();
1288
+ this._modelBindingIds.clear();
1289
+ this._particles.length = 0;
1290
+ this._geometries.clear();
1291
+ }
1292
+ async _initialize(options) {
1293
+ this._createMaterials();
1294
+ this._createNodes(options);
1295
+ this._attachHierarchy();
1296
+ this._scene.add(this.root);
1297
+ if (options.addPreviewLights !== false)
1298
+ this._addPreviewLights();
1299
+ await this._loadModels(options);
1300
+ this._buildClipPartitions();
1301
+ this._createStateMachineRuntime();
1302
+ const authoredCamera = this._cameras.keys().next().value;
1303
+ if (options.useAuthoredCamera !== false && authoredCamera)
1304
+ this.useAuthoredCamera(authoredCamera);
1305
+ this._initialized = true;
1306
+ }
1307
+ _createMaterials() {
1308
+ for (const descriptor of this._payload.materials) {
1309
+ this._materials.set(descriptor.id, materialFromDescriptor(descriptor));
1310
+ }
1311
+ }
1312
+ _createNodes(options) {
1313
+ for (const descriptor of this._payload.nodes) {
1314
+ const entity = new Entity(descriptor.name || descriptor.id);
1315
+ const transform = new Transform3D();
1316
+ const state = {
1317
+ transform,
1318
+ translation: new Float32Array(descriptor.transform.translation),
1319
+ rotation: new Float32Array(descriptor.transform.rotation),
1320
+ scale: new Float32Array(descriptor.transform.scale),
1321
+ matrix: new Float32Array(16),
1322
+ };
1323
+ commitTransform(state);
1324
+ entity.addComponent(transform);
1325
+ this._entities.set(descriptor.id, entity);
1326
+ this._transforms.set(descriptor.id, state);
1327
+ for (const component of descriptor.components)
1328
+ this._createComponent(descriptor, entity, component, options);
1329
+ }
1330
+ }
1331
+ _createComponent(node, entity, component, options) {
1332
+ if (component.kind === 'camera3d') {
1333
+ const camera = cameraFromProjection(component.projection, this._payload.viewport.width / this._payload.viewport.height);
1334
+ entity.addComponent(camera);
1335
+ this._cameras.set(node.id, camera);
1336
+ return;
1337
+ }
1338
+ if (component.kind === 'primitive3d') {
1339
+ const geometry = primitiveGeometry(component.primitive);
1340
+ this._geometries.add(geometry);
1341
+ entity.addComponent(new Mesh3D(geometry, required(this._materials, component.materialId, 'material')));
1342
+ return;
1343
+ }
1344
+ if (component.kind === 'particle3d') {
1345
+ const { textureResource, ...descriptor } = component.descriptor;
1346
+ const resource = textureResource ? required(this._resources, textureResource, 'particle texture') : undefined;
1347
+ const textureSource = resource ? options.resolveTexture?.(resource, null) ?? null : null;
1348
+ const emitter = new ParticleEmitter3D({
1349
+ ...descriptor,
1350
+ ...(textureSource ? { textureSource } : {}),
1351
+ ...(this._payload.stateMachine ? { playing: false, emitting: false } : {}),
1352
+ });
1353
+ entity.addComponent(emitter);
1354
+ this._particles.push({
1355
+ key: `${node.id}\u0000${component.id}`,
1356
+ node,
1357
+ componentId: component.id,
1358
+ emitter,
1359
+ });
1360
+ }
1361
+ }
1362
+ _attachHierarchy() {
1363
+ for (const descriptor of this._payload.nodes) {
1364
+ const entity = required(this._entities, descriptor.id, 'node');
1365
+ if (descriptor.parent)
1366
+ required(this._entities, descriptor.parent, 'parent node').addChild(entity);
1367
+ else
1368
+ this.root.addChild(entity);
1369
+ }
1370
+ }
1371
+ async _loadModels(options) {
1372
+ const loader = options.loadModel ?? loadGltfModel;
1373
+ const tasks = [];
1374
+ for (const node of this._payload.nodes) {
1375
+ for (const component of node.components) {
1376
+ if (component.kind !== 'model3d')
1377
+ continue;
1378
+ tasks.push((async () => {
1379
+ const resource = required(this._resources, component.resource, 'model resource');
1380
+ const model = await loader(resource.uri, { signal: this._abortController.signal });
1381
+ if (this._state !== 'active') {
1382
+ if (!model.root.destroyed)
1383
+ this._disposeModel(model);
1384
+ throw abortError('runtime-destroyed-during-model-load');
1385
+ }
1386
+ const entity = required(this._entities, node.id, 'model node');
1387
+ entity.addChild(model.root);
1388
+ applyMaterialOverrides(model.root, component.materialOverrides ?? [], this._materials);
1389
+ const runtime = new GltfAnimation3DRuntime(model, {
1390
+ signal: this._abortController.signal,
1391
+ clipIdPrefix: `hya:${component.id}`,
1392
+ });
1393
+ this._models.push({ nodeId: node.id, componentId: component.id, model, runtime });
1394
+ })());
1395
+ }
1396
+ }
1397
+ await Promise.all(tasks);
1398
+ }
1399
+ _buildClipPartitions() {
1400
+ for (const clip of this._clips) {
1401
+ const mainTracks = [];
1402
+ const modelTracks = new Map();
1403
+ for (const track of clip.tracks) {
1404
+ const endpoint = this._createSceneEndpoint(track.binding);
1405
+ if (endpoint) {
1406
+ this._resolver.add(endpoint);
1407
+ mainTracks.push(track);
1408
+ continue;
1409
+ }
1410
+ const modelMatch = this._matchModelBinding(track);
1411
+ if (modelMatch) {
1412
+ let tracks = modelTracks.get(modelMatch.model);
1413
+ if (!tracks) {
1414
+ tracks = [];
1415
+ modelTracks.set(modelMatch.model, tracks);
1416
+ }
1417
+ tracks.push({ ...track, binding: modelMatch.binding });
1418
+ let bindingIds = this._modelBindingIds.get(modelMatch.model);
1419
+ if (!bindingIds) {
1420
+ bindingIds = new Map();
1421
+ this._modelBindingIds.set(modelMatch.model, bindingIds);
1422
+ }
1423
+ bindingIds.set(track.binding.id, modelMatch.binding.id);
1424
+ continue;
1425
+ }
1426
+ throw new RangeError(`HYA Animation3D binding "${track.binding.id}" cannot be resolved.`);
1427
+ }
1428
+ if (mainTracks.length > 0 || this._payload.stateMachine) {
1429
+ this._mainClips.set(clip.id, { ...clip, tracks: Object.freeze(mainTracks) });
1430
+ }
1431
+ const byModel = new Map();
1432
+ for (const [model, tracks] of modelTracks)
1433
+ byModel.set(model, { ...clip, id: `${clip.id}:${model.componentId}`, tracks: Object.freeze(tracks) });
1434
+ this._modelClips.set(clip.id, byModel);
1435
+ }
1436
+ }
1437
+ _createStateMachineRuntime() {
1438
+ const definition = this._payload.stateMachine;
1439
+ if (!definition)
1440
+ return;
1441
+ const partitions = [{
1442
+ id: 'scene',
1443
+ mixer: animation3DMixerRuntime(this.mixer),
1444
+ clips: this._mainClips,
1445
+ pose: this.pose,
1446
+ apply: pose => this._poseApplier.apply(pose),
1447
+ }];
1448
+ for (const model of this._models) {
1449
+ const clips = new Map();
1450
+ for (const [clipId, byModel] of this._modelClips) {
1451
+ const clip = byModel.get(model);
1452
+ if (clip)
1453
+ clips.set(clipId, clip);
1454
+ }
1455
+ if (clips.size === 0)
1456
+ continue;
1457
+ partitions.push({
1458
+ id: `model:${model.nodeId}:${model.componentId}`,
1459
+ mixer: animation3DMixerRuntime(model.runtime.mixer),
1460
+ clips,
1461
+ ...(this._modelBindingIds.get(model) ? { bindingIds: this._modelBindingIds.get(model) } : {}),
1462
+ pose: model.runtime.pose,
1463
+ apply: pose => model.runtime.applySynchronizedPose(pose),
1464
+ });
1465
+ }
1466
+ const particleCues = new Map();
1467
+ for (const clip of this._clips) {
1468
+ const cues = [];
1469
+ for (const particle of this._particles) {
1470
+ const start = Math.min(clip.duration, Math.max(0, particle.node.start ?? 0));
1471
+ const authoredEnd = particle.node.duration === undefined
1472
+ ? clip.duration
1473
+ : (particle.node.start ?? 0) + particle.node.duration;
1474
+ const end = Math.min(clip.duration, Math.max(0, authoredEnd));
1475
+ if (end <= start)
1476
+ continue;
1477
+ cues.push(Object.freeze({
1478
+ key: particle.key,
1479
+ emitter: particle.emitter,
1480
+ start,
1481
+ end,
1482
+ }));
1483
+ }
1484
+ particleCues.set(clip.id, Object.freeze(cues));
1485
+ }
1486
+ this._stateMachine = new HyaAnimation3DStateMachineRuntime({
1487
+ definition,
1488
+ clips: this._clips,
1489
+ partitions,
1490
+ particleCues,
1491
+ });
1492
+ }
1493
+ _createSceneEndpoint(binding) {
1494
+ if (binding.path === 'morph.weights')
1495
+ return null;
1496
+ const nodeId = binding.target.kind === 'node-id' ? binding.target.nodeId
1497
+ : binding.target.kind === 'node-path' && binding.target.segments.length === 1 ? binding.target.segments[0]
1498
+ : null;
1499
+ if (binding.path.startsWith('transform.') && nodeId) {
1500
+ const state = this._transforms.get(nodeId);
1501
+ if (!state)
1502
+ return null;
1503
+ const target = binding.path === 'transform.translation' ? state.translation
1504
+ : binding.path === 'transform.rotation' ? state.rotation : state.scale;
1505
+ return endpoint(binding, new Float32Array(target), value => { copy(value, target, binding.valueSize); commitTransform(state); });
1506
+ }
1507
+ if (binding.path !== 'property')
1508
+ return null;
1509
+ if (binding.component === 'material3d' && binding.target.kind === 'slot') {
1510
+ const material = this._materials.get(binding.target.slot);
1511
+ if (!material)
1512
+ return null;
1513
+ return materialEndpoint(binding, material);
1514
+ }
1515
+ if (binding.component === 'camera3d' && nodeId) {
1516
+ const camera = this._cameras.get(nodeId);
1517
+ if (!camera)
1518
+ return null;
1519
+ return cameraEndpoint(binding, camera, this._payload.viewport.width / this._payload.viewport.height);
1520
+ }
1521
+ return null;
1522
+ }
1523
+ _matchModelBinding(track) {
1524
+ const candidates = [];
1525
+ for (const model of this._models) {
1526
+ for (const sourceClip of model.runtime.clips) {
1527
+ for (const sourceTrack of sourceClip.tracks) {
1528
+ if (sameModelBinding(track.binding, sourceTrack.binding, model.nodeId))
1529
+ candidates.push({ model, binding: sourceTrack.binding });
1530
+ }
1531
+ }
1532
+ }
1533
+ const exact = candidates.find(candidate => candidate.binding.id === track.binding.id);
1534
+ if (exact)
1535
+ return exact;
1536
+ return candidates.length === 1 ? candidates[0] : null;
1537
+ }
1538
+ _addPreviewLights() {
1539
+ const ambient = new Entity('HYA Preview Ambient');
1540
+ ambient.addComponent(new AmbientLight({ color: [0.72, 0.8, 1], intensity: 0.5 }));
1541
+ this.root.addChild(ambient);
1542
+ const sun = new Entity('HYA Preview Sun');
1543
+ sun.addComponent(new DirectionalLight({ color: [1, 0.94, 0.84], intensity: 2, direction: [-0.4, -1, -0.35], castShadow: false }));
1544
+ this.root.addChild(sun);
1545
+ }
1546
+ _assertActive() {
1547
+ if (this._state !== 'active' || this.root.destroyed) {
1548
+ this.destroy();
1549
+ throw new Error('HYA Animation3D runtime is destroyed.');
1550
+ }
1551
+ }
1552
+ }
1553
+ async function createHyaAnimation3DRuntime(options) {
1554
+ return await HyaAnimation3DRuntime.create(options);
1555
+ }
1556
+ function lowerRuntimeClip(value) {
1557
+ return Object.freeze({
1558
+ format: 'haiyue-animation3d-clip@1',
1559
+ id: value.id,
1560
+ name: value.name,
1561
+ duration: value.duration,
1562
+ tracks: Object.freeze(value.tracks.map(track => Object.freeze({
1563
+ id: track.id,
1564
+ binding: track.binding,
1565
+ interpolation: track.interpolation,
1566
+ times: new Float32Array(Array.from(track.times)),
1567
+ values: new Float32Array(Array.from(track.values)),
1568
+ }))),
1569
+ events: Object.freeze([...value.events]),
1570
+ });
1571
+ }
1572
+ function materialFromDescriptor(value) {
1573
+ return new PbrMaterial({
1574
+ baseColor: value.baseColorFactor,
1575
+ metallic: value.metallicFactor,
1576
+ roughness: Math.max(0.04, value.roughnessFactor),
1577
+ emissiveFactor: value.emissiveFactor,
1578
+ alphaMode: value.alphaMode,
1579
+ alphaCutoff: value.alphaCutoff ?? 0.5,
1580
+ doubleSided: value.doubleSided,
1581
+ });
1582
+ }
1583
+ function cameraFromProjection(value, aspect) {
1584
+ if (value.kind === 'perspective')
1585
+ return new Camera3D({ type: 'perspective', fov: value.fovYRadians, near: value.near, far: value.far, aspect });
1586
+ const half = value.orthoHeight * 0.5;
1587
+ return new Camera3D({ type: 'orthographic', near: value.near, far: value.far, aspect, left: -half * aspect, right: half * aspect, top: half, bottom: -half });
1588
+ }
1589
+ function primitiveGeometry(value) {
1590
+ switch (value) {
1591
+ case 'box': return createBox3D();
1592
+ case 'sphere': return createSphere3D();
1593
+ case 'plane': return createPlane3D();
1594
+ case 'cylinder': return createCylinder3D();
1595
+ case 'cone': return createCone3D();
1596
+ }
1597
+ }
1598
+ function commitTransform(state) {
1599
+ composeTrsMatrix(state.translation, state.rotation, state.scale, state.matrix);
1600
+ state.transform.localMatrix = state.matrix;
1601
+ }
1602
+ function endpoint(binding, base, write) {
1603
+ return new HyaAnimation3DResolvedEndpoint(binding, out => copy(base, out, binding.valueSize), write);
1604
+ }
1605
+ function materialEndpoint(binding, material) {
1606
+ const base = new Float32Array(binding.valueSize);
1607
+ if (binding.property === 'baseColorFactor')
1608
+ material.baseColor.writeLinear(base);
1609
+ else {
1610
+ const value = binding.property === 'emissiveFactor' ? material.emissiveFactor
1611
+ : [binding.property === 'metallicFactor' ? material.metallic
1612
+ : binding.property === 'roughnessFactor' ? material.roughness
1613
+ : material.alphaCutoff];
1614
+ copy(value, base, binding.valueSize);
1615
+ }
1616
+ const write = (value) => {
1617
+ if (binding.property === 'baseColorFactor')
1618
+ material.baseColor = tuple4(value);
1619
+ else if (binding.property === 'emissiveFactor')
1620
+ material.emissiveFactor = tuple3(value);
1621
+ else if (binding.property === 'metallicFactor')
1622
+ material.metallic = value[0] ?? 0;
1623
+ else if (binding.property === 'roughnessFactor')
1624
+ material.roughness = Math.max(0.04, value[0] ?? 0.04);
1625
+ else if (binding.property === 'alphaCutoff')
1626
+ material.alphaCutoff = value[0] ?? 0.5;
1627
+ };
1628
+ return endpoint(binding, base, write);
1629
+ }
1630
+ function cameraEndpoint(binding, camera, aspect) {
1631
+ const base = new Float32Array([binding.property === 'fovYRadians' ? camera.fov
1632
+ : binding.property === 'near' ? camera.near
1633
+ : binding.property === 'far' ? camera.far
1634
+ : camera.orthoTop - camera.orthoBottom]);
1635
+ const write = (value) => {
1636
+ const next = value[0] ?? 0;
1637
+ if (binding.property === 'fovYRadians')
1638
+ camera.fov = next;
1639
+ else if (binding.property === 'near')
1640
+ camera.near = next;
1641
+ else if (binding.property === 'far')
1642
+ camera.far = next;
1643
+ else if (binding.property === 'orthoHeight') {
1644
+ const half = next * 0.5;
1645
+ camera.orthoLeft = -half * aspect;
1646
+ camera.orthoRight = half * aspect;
1647
+ camera.orthoTop = half;
1648
+ camera.orthoBottom = -half;
1649
+ }
1650
+ };
1651
+ return endpoint(binding, base, write);
1652
+ }
1653
+ function sameModelBinding(candidate, endpoint, modelNodeId) {
1654
+ if (candidate.id === endpoint.id)
1655
+ return candidate.path === endpoint.path && candidate.valueSize === endpoint.valueSize;
1656
+ if (candidate.path !== endpoint.path || candidate.valueSize !== endpoint.valueSize)
1657
+ return false;
1658
+ if (candidate.target.kind === 'node-path' && endpoint.target.kind === 'node-path') {
1659
+ const authored = candidate.target.segments[0] === modelNodeId ? candidate.target.segments.slice(1) : candidate.target.segments;
1660
+ const endpointSegments = endpoint.target.segments;
1661
+ return authored.length <= endpointSegments.length
1662
+ && authored.every((segment, index) => segment === endpointSegments[endpointSegments.length - authored.length + index]);
1663
+ }
1664
+ if (candidate.path === 'morph.weights' && candidate.target.kind === 'slot') {
1665
+ return candidate.target.slot === modelNodeId || candidate.target.slot.startsWith(`${modelNodeId}:`);
1666
+ }
1667
+ return false;
1668
+ }
1669
+ function applyMaterialOverrides(root, overrides, materials) {
1670
+ if (overrides.length === 0)
1671
+ return;
1672
+ const bySlot = new Map(overrides.map(item => [item.slot, required(materials, item.materialId, 'material override')]));
1673
+ const pending = [root];
1674
+ while (pending.length > 0) {
1675
+ const entity = pending.pop();
1676
+ const mesh = entity.getComponent(Mesh3D);
1677
+ const material = bySlot.get(entity.name);
1678
+ if (mesh && material)
1679
+ mesh.material = material;
1680
+ pending.push(...entity.children);
1681
+ }
1682
+ }
1683
+ function copy(source, target, count) {
1684
+ for (let index = 0; index < count; index++)
1685
+ target[index] = source[index] ?? 0;
1686
+ }
1687
+ function tuple3(value) {
1688
+ return [value[0] ?? 0, value[1] ?? 0, value[2] ?? 0];
1689
+ }
1690
+ function tuple4(value) {
1691
+ return [value[0] ?? 0, value[1] ?? 0, value[2] ?? 0, value[3] ?? 0];
1692
+ }
1693
+ function withoutGroupOptions(options) {
1694
+ const { fadeFrom: _fadeFrom, fadeDuration: _fadeDuration, warp: _warp, id: _id, ...result } = options;
1695
+ return result;
1696
+ }
1697
+ function required(map, key, label) {
1698
+ const value = map.get(key);
1699
+ if (!value)
1700
+ throw new RangeError(`Unknown ${label} "${String(key)}".`);
1701
+ return value;
1702
+ }
1703
+ function abortError(reason) {
1704
+ return new DOMException(reason === undefined ? 'The operation was aborted.' : String(reason), 'AbortError');
1705
+ }
1706
+
1707
+ export { Animation3DError, Animation3DMixer, Animation3DPoseApplier, Animation3DPoseBuffer, Animation3DStateMachineController, Animation3DStateMachineValidationError, HyaAnimation3DRuntime, compileAnimation3DStateMachineDefinition, createHyaAnimation3DRuntime, validateAnimation3DStateMachineDefinition };
1708
+ //# sourceMappingURL=animation3d.js.map