@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,756 @@
1
+ import {
2
+ Camera3D,
3
+ Component,
4
+ Entity,
5
+ Mesh3D,
6
+ type Scene,
7
+ } from '@haiyue/engine';
8
+ import { MeshHelper, ParticleEmitter3D, Transform3D } from '@haiyue/engine/components';
9
+ import {
10
+ createBox3D,
11
+ createCone3D,
12
+ createCylinder3D,
13
+ createPlane3D,
14
+ createSphere3D,
15
+ type Geometry3D,
16
+ } from '@haiyue/engine/geometry';
17
+ import { AmbientLight, DirectionalLight } from '@haiyue/engine/lighting';
18
+ import { PbrMaterial } from '@haiyue/engine/material';
19
+ import { disposeGltfModel, loadGltfModel } from '../../gltf/gltfLoader';
20
+ import type { LoadedGltfModel } from '../../gltf/GltfLoaderContract';
21
+ import { GltfAnimation3DRuntime } from '../../gltf/GltfAnimation3DAdapter';
22
+ import { composeTrsMatrix } from '../../gltf/GltfAnimationRuntime';
23
+ import type {
24
+ Animation3DBinding,
25
+ Animation3DBindingResolver,
26
+ Animation3DResolvedBinding,
27
+ } from '../Animation3DBinding';
28
+ import type { Animation3DAction } from '../Animation3DAction';
29
+ import type { Animation3DClip } from '../Animation3DClip';
30
+ import { Animation3DMixer } from '../Animation3DMixer';
31
+ import { Animation3DPoseApplier } from '../Animation3DPose';
32
+ import { Animation3DPoseBuffer } from '../Animation3DPoseBuffer';
33
+ import type { Animation3DTrack } from '../Animation3DTrack';
34
+ import { animation3DMixerRuntime } from '../runtime/mixer/Animation3DMixerRuntimeStore';
35
+ import {
36
+ HyaAnimation3DStateMachineRuntime,
37
+ type HyaAnimation3DParticleCue,
38
+ type HyaAnimation3DStateMachinePartition,
39
+ } from './HyaAnimation3DStateMachineRuntime';
40
+ import type {
41
+ HyaAnimation3DActionGroup,
42
+ HyaAnimation3DCameraProjection,
43
+ HyaAnimation3DComponentDescriptor,
44
+ HyaAnimation3DMaterialDescriptor,
45
+ HyaAnimation3DNodeDescriptor,
46
+ HyaAnimation3DPayload,
47
+ HyaAnimation3DPlayOptions,
48
+ HyaAnimation3DResource,
49
+ HyaAnimation3DRuntimeDiagnostics,
50
+ HyaAnimation3DRuntimeOptions,
51
+ HyaAnimation3DStateMachineRuntimeControl,
52
+ } from './HyaAnimation3DTypes';
53
+
54
+ type RuntimeState = 'active' | 'destroyed';
55
+
56
+ interface TransformState {
57
+ readonly transform: Transform3D;
58
+ readonly translation: Float32Array;
59
+ readonly rotation: Float32Array;
60
+ readonly scale: Float32Array;
61
+ readonly matrix: Float32Array;
62
+ }
63
+
64
+ interface ModelState {
65
+ readonly nodeId: string;
66
+ readonly componentId: string;
67
+ readonly model: LoadedGltfModel;
68
+ readonly runtime: GltfAnimation3DRuntime;
69
+ }
70
+
71
+ interface ActionEntry {
72
+ readonly owner: Animation3DMixer;
73
+ readonly action: Animation3DAction;
74
+ }
75
+
76
+ interface ParticleState {
77
+ readonly key: string;
78
+ readonly node: HyaAnimation3DNodeDescriptor;
79
+ readonly componentId: string;
80
+ readonly emitter: ParticleEmitter3D;
81
+ }
82
+
83
+ class HyaAnimation3DResolvedEndpoint implements Animation3DResolvedBinding {
84
+ constructor(
85
+ readonly binding: Animation3DBinding,
86
+ private readonly _read: (out: Float32Array) => void,
87
+ private readonly _write: (value: ArrayLike<number>) => void,
88
+ ) {}
89
+
90
+ read(out: Float32Array): void { this._read(out); }
91
+ write(value: ArrayLike<number>): void { this._write(value); }
92
+ }
93
+
94
+ class HyaAnimation3DBindingResolver implements Animation3DBindingResolver {
95
+ private readonly _endpoints = new Map<string, HyaAnimation3DResolvedEndpoint>();
96
+ private _revision = 0;
97
+ private _destroyed = false;
98
+
99
+ get revision(): number { return this._revision; }
100
+ get size(): number { return this._endpoints.size; }
101
+
102
+ add(endpoint: HyaAnimation3DResolvedEndpoint): void {
103
+ if (this._destroyed) throw new Error('HYA Animation3D resolver is destroyed.');
104
+ this._endpoints.set(endpoint.binding.id, endpoint);
105
+ this._revision++;
106
+ }
107
+
108
+ resolve<TBinding extends Animation3DBinding>(binding: TBinding): Animation3DResolvedBinding<TBinding> | null {
109
+ if (this._destroyed) return null;
110
+ const endpoint = this._endpoints.get(binding.id);
111
+ if (!endpoint || endpoint.binding.path !== binding.path
112
+ || endpoint.binding.valueType !== binding.valueType
113
+ || endpoint.binding.valueSize !== binding.valueSize) return null;
114
+ return endpoint as unknown as Animation3DResolvedBinding<TBinding>;
115
+ }
116
+
117
+ destroy(): void {
118
+ if (this._destroyed) return;
119
+ this._destroyed = true;
120
+ this._endpoints.clear();
121
+ this._revision++;
122
+ }
123
+ }
124
+
125
+ class HyaAnimation3DRuntimeOwner extends Component {
126
+ private _runtime: HyaAnimation3DRuntime | null = null;
127
+
128
+ constructor() { super('HyaAnimation3DRuntimeOwner'); }
129
+ attach(runtime: HyaAnimation3DRuntime): void { this._runtime = runtime; }
130
+ release(runtime: HyaAnimation3DRuntime): void { if (this._runtime === runtime) this._runtime = null; }
131
+ override clone(): HyaAnimation3DRuntimeOwner { return new HyaAnimation3DRuntimeOwner(); }
132
+ override destroy(): void {
133
+ if (this.destroyed) return;
134
+ const runtime = this._runtime;
135
+ this._runtime = null;
136
+ runtime?.destroy();
137
+ super.destroy();
138
+ }
139
+ }
140
+
141
+ class HyaAnimation3DActionGroupImpl implements HyaAnimation3DActionGroup {
142
+ constructor(
143
+ readonly id: string,
144
+ readonly clipId: string,
145
+ readonly entries: readonly ActionEntry[],
146
+ ) {}
147
+
148
+ get actionCount(): number { return this.entries.length; }
149
+ get playing(): boolean {
150
+ return this.entries.some(entry => entry.owner.state === 'active'
151
+ && (entry.action.status === 'running' || entry.action.status === 'scheduled'));
152
+ }
153
+ play(): this { for (const entry of this.entries) if (entry.owner.state === 'active') entry.action.play(); return this; }
154
+ stop(): this { for (const entry of this.entries) if (entry.owner.state === 'active') entry.action.stop(); return this; }
155
+ reset(): this { for (const entry of this.entries) if (entry.owner.state === 'active') entry.action.reset(); return this; }
156
+ }
157
+
158
+ /**
159
+ * Optional native-3D HYA owner. The caller must pass an already validated
160
+ * animation-spec payload; no ECS/GPU object is allocated by the parser.
161
+ */
162
+ export class HyaAnimation3DRuntime {
163
+ readonly root = new Entity('HYA Native 3D');
164
+ readonly mixer: Animation3DMixer;
165
+ readonly pose = new Animation3DPoseBuffer();
166
+
167
+ private readonly _scene: Scene;
168
+ private readonly _payload: HyaAnimation3DPayload;
169
+ private readonly _clips: readonly Animation3DClip[];
170
+ private readonly _resources: Map<string, HyaAnimation3DResource>;
171
+ private readonly _entities = new Map<string, Entity>();
172
+ private readonly _transforms = new Map<string, TransformState>();
173
+ private readonly _materials = new Map<string, PbrMaterial>();
174
+ private readonly _cameras = new Map<string, Camera3D>();
175
+ private readonly _models: ModelState[] = [];
176
+ private readonly _mainClips = new Map<string, Animation3DClip>();
177
+ private readonly _modelClips = new Map<string, Map<ModelState, Animation3DClip>>();
178
+ private readonly _modelBindingIds = new Map<ModelState, Map<string, string>>();
179
+ private readonly _actionGroups = new Map<string, HyaAnimation3DActionGroupImpl>();
180
+ private readonly _resolver = new HyaAnimation3DBindingResolver();
181
+ private readonly _poseApplier: Animation3DPoseApplier;
182
+ private readonly _owner = new HyaAnimation3DRuntimeOwner();
183
+ private readonly _geometries = new Set<Geometry3D>();
184
+ private readonly _particles: ParticleState[] = [];
185
+ private readonly _abortController = new AbortController();
186
+ private readonly _externalSignal: AbortSignal | null;
187
+ private readonly _externalAbortListener: (() => void) | null;
188
+ private readonly _disposeModel: (model: LoadedGltfModel) => void;
189
+ private _state: RuntimeState = 'active';
190
+ private _initialized = false;
191
+ private _stateMachine: HyaAnimation3DStateMachineRuntime | null = null;
192
+
193
+ private constructor(options: HyaAnimation3DRuntimeOptions) {
194
+ this._scene = options.scene;
195
+ this._payload = options.payload;
196
+ this._clips = Object.freeze(options.payload.clips.map(lowerRuntimeClip));
197
+ this._resources = new Map(options.resources.map(resource => [resource.id, resource]));
198
+ this._disposeModel = options.disposeModel ?? disposeGltfModel;
199
+ this.mixer = new Animation3DMixer(this._resolver);
200
+ this._poseApplier = new Animation3DPoseApplier(this._resolver);
201
+ this._owner.attach(this);
202
+ this.root.addComponent(this._owner);
203
+ this._externalSignal = options.signal ?? null;
204
+ this._externalAbortListener = this._externalSignal ? () => this.destroy() : null;
205
+ this._externalSignal?.addEventListener('abort', this._externalAbortListener!, { once: true });
206
+ }
207
+
208
+ static async create(options: HyaAnimation3DRuntimeOptions): Promise<HyaAnimation3DRuntime> {
209
+ if (options.signal?.aborted) throw abortError(options.signal.reason);
210
+ const runtime = new HyaAnimation3DRuntime(options);
211
+ try {
212
+ await runtime._initialize(options);
213
+ return runtime;
214
+ } catch (error) {
215
+ runtime.destroy();
216
+ throw error;
217
+ }
218
+ }
219
+
220
+ get state(): RuntimeState { return this._state; }
221
+ get initialized(): boolean { return this._initialized; }
222
+ get clips(): readonly Animation3DClip[] { return this._clips; }
223
+ get authoredCameraIds(): readonly string[] { return Object.freeze([...this._cameras.keys()]); }
224
+ get stateMachine(): HyaAnimation3DStateMachineRuntimeControl | null { return this._stateMachine; }
225
+
226
+ getEntity(nodeId: string): Entity | null { return this._entities.get(nodeId) ?? null; }
227
+ getMaterial(materialId: string): PbrMaterial | null { return this._materials.get(materialId) ?? null; }
228
+ getModelAnimationClips(nodeId: string): readonly Animation3DClip[] {
229
+ return this._models.find(model => model.nodeId === nodeId)?.runtime.clips ?? Object.freeze([]);
230
+ }
231
+
232
+ select(nodeIds: readonly string[]): void {
233
+ const selected = new Set(nodeIds);
234
+ for (const [id, entity] of this._entities) {
235
+ if (selected.has(id)) entity.addComponent(new MeshHelper({ mode: 'aabb', color: [0.25, 0.75, 1, 1], lineWidth: 1.5 }));
236
+ else entity.removeComponent(MeshHelper);
237
+ }
238
+ }
239
+
240
+ useAuthoredCamera(nodeId: string): void {
241
+ this._assertActive();
242
+ const entity = this._entities.get(nodeId);
243
+ if (!entity || !this._cameras.has(nodeId)) throw new RangeError(`Unknown authored camera node "${nodeId}".`);
244
+ this._scene.setCamera(entity);
245
+ }
246
+
247
+ playClip(clipId: string, options: HyaAnimation3DPlayOptions = {}): HyaAnimation3DActionGroup {
248
+ this._assertActive();
249
+ if (this._stateMachine) {
250
+ throw new Error('Manual HYA Animation3D clip actions are unavailable while the built-in state machine owns the mixers.');
251
+ }
252
+ const base = this._clips.find(clip => clip.id === clipId);
253
+ if (!base) throw new RangeError(`Unknown HYA Animation3D clip "${clipId}".`);
254
+ const groupId = options.id ?? `${clipId}:action:${this._actionGroups.size}`;
255
+ if (this._actionGroups.has(groupId)) throw new RangeError(`HYA Animation3D action group "${groupId}" already exists.`);
256
+ const entries: ActionEntry[] = [];
257
+ const actionOptions = withoutGroupOptions(options);
258
+ const main = this._mainClips.get(clipId);
259
+ if (main) entries.push({ owner: this.mixer, action: this.mixer.createAction(main, { ...actionOptions, id: `${groupId}:scene` }) });
260
+ for (const [model, clip] of this._modelClips.get(clipId) ?? []) {
261
+ entries.push({ owner: model.runtime.mixer, action: model.runtime.mixer.createAction(clip, { ...actionOptions, id: `${groupId}:${model.componentId}` }) });
262
+ }
263
+ const group = new HyaAnimation3DActionGroupImpl(groupId, clipId, entries);
264
+ if (options.fadeFrom) {
265
+ const source = options.fadeFrom as HyaAnimation3DActionGroupImpl;
266
+ const duration = options.fadeDuration ?? 0;
267
+ for (const entry of entries) {
268
+ const sourceEntry = source.entries.find(candidate => candidate.owner === entry.owner);
269
+ if (sourceEntry) entry.action.crossFadeFrom(sourceEntry.action, duration, options.warp ?? false);
270
+ else entry.action.fadeIn(duration).play();
271
+ }
272
+ } else group.play();
273
+ this._actionGroups.set(groupId, group);
274
+ return group;
275
+ }
276
+
277
+ removeActionGroup(group: HyaAnimation3DActionGroup | string): boolean {
278
+ const id = typeof group === 'string' ? group : group.id;
279
+ const target = this._actionGroups.get(id);
280
+ if (!target) return false;
281
+ for (const entry of target.entries) {
282
+ if (entry.owner.state !== 'active') continue;
283
+ entry.action.stop();
284
+ entry.owner.removeAction(entry.action);
285
+ }
286
+ this._actionGroups.delete(id);
287
+ return true;
288
+ }
289
+
290
+ update(deltaSeconds: number): void {
291
+ this._assertActive();
292
+ if (this._stateMachine) {
293
+ this._stateMachine.update(deltaSeconds);
294
+ return;
295
+ }
296
+ this._poseApplier.apply(this.mixer.update(deltaSeconds, this.pose));
297
+ for (const model of this._models) model.runtime.update(deltaSeconds);
298
+ }
299
+
300
+ setTime(timeSeconds: number): void {
301
+ this._assertActive();
302
+ if (this._stateMachine) {
303
+ throw new Error('HYA Animation3D setTime() is unavailable while the built-in state machine owns the playhead.');
304
+ }
305
+ this._poseApplier.apply(this.mixer.setTime(timeSeconds, this.pose));
306
+ for (const model of this._models) model.runtime.setTime(timeSeconds);
307
+ }
308
+
309
+ diagnostics(): HyaAnimation3DRuntimeDiagnostics {
310
+ const residual = this._state === 'destroyed'
311
+ ? this._entities.size + this._materials.size + this._models.length + this._actionGroups.size + this._resolver.size
312
+ : 0;
313
+ return Object.freeze({
314
+ state: this._state,
315
+ entityCount: this._entities.size,
316
+ materialCount: this._materials.size,
317
+ modelCount: this._models.length,
318
+ actionGroupCount: this._actionGroups.size,
319
+ ownerResidualCount: residual,
320
+ });
321
+ }
322
+
323
+ destroy(): void {
324
+ if (this._state === 'destroyed') return;
325
+ this._state = 'destroyed';
326
+ if (this._externalSignal && this._externalAbortListener) this._externalSignal.removeEventListener('abort', this._externalAbortListener);
327
+ this._stateMachine?.destroy();
328
+ this._stateMachine = null;
329
+ for (const id of [...this._actionGroups.keys()]) this.removeActionGroup(id);
330
+ this._abortController.abort('hya-animation3d-runtime-destroyed');
331
+ this.mixer.destroy();
332
+ this._resolver.destroy();
333
+ for (const model of this._models.splice(0)) {
334
+ model.runtime.destroy();
335
+ if (!model.model.root.destroyed) this._disposeModel(model.model);
336
+ }
337
+ this._owner.release(this);
338
+ if (!this.root.destroyed) this.root.destroy();
339
+ this._entities.clear();
340
+ this._transforms.clear();
341
+ this._materials.clear();
342
+ this._cameras.clear();
343
+ this._mainClips.clear();
344
+ this._modelClips.clear();
345
+ this._modelBindingIds.clear();
346
+ this._particles.length = 0;
347
+ this._geometries.clear();
348
+ }
349
+
350
+ private async _initialize(options: HyaAnimation3DRuntimeOptions): Promise<void> {
351
+ this._createMaterials();
352
+ this._createNodes(options);
353
+ this._attachHierarchy();
354
+ this._scene.add(this.root);
355
+ if (options.addPreviewLights !== false) this._addPreviewLights();
356
+ await this._loadModels(options);
357
+ this._buildClipPartitions();
358
+ this._createStateMachineRuntime();
359
+ const authoredCamera = this._cameras.keys().next().value as string | undefined;
360
+ if (options.useAuthoredCamera !== false && authoredCamera) this.useAuthoredCamera(authoredCamera);
361
+ this._initialized = true;
362
+ }
363
+
364
+ private _createMaterials(): void {
365
+ for (const descriptor of this._payload.materials) {
366
+ this._materials.set(descriptor.id, materialFromDescriptor(descriptor));
367
+ }
368
+ }
369
+
370
+ private _createNodes(options: HyaAnimation3DRuntimeOptions): void {
371
+ for (const descriptor of this._payload.nodes) {
372
+ const entity = new Entity(descriptor.name || descriptor.id);
373
+ const transform = new Transform3D();
374
+ const state: TransformState = {
375
+ transform,
376
+ translation: new Float32Array(descriptor.transform.translation),
377
+ rotation: new Float32Array(descriptor.transform.rotation),
378
+ scale: new Float32Array(descriptor.transform.scale),
379
+ matrix: new Float32Array(16),
380
+ };
381
+ commitTransform(state);
382
+ entity.addComponent(transform);
383
+ this._entities.set(descriptor.id, entity);
384
+ this._transforms.set(descriptor.id, state);
385
+ for (const component of descriptor.components) this._createComponent(descriptor, entity, component, options);
386
+ }
387
+ }
388
+
389
+ private _createComponent(
390
+ node: HyaAnimation3DNodeDescriptor,
391
+ entity: Entity,
392
+ component: HyaAnimation3DComponentDescriptor,
393
+ options: HyaAnimation3DRuntimeOptions,
394
+ ): void {
395
+ if (component.kind === 'camera3d') {
396
+ const camera = cameraFromProjection(component.projection, this._payload.viewport.width / this._payload.viewport.height);
397
+ entity.addComponent(camera);
398
+ this._cameras.set(node.id, camera);
399
+ return;
400
+ }
401
+ if (component.kind === 'primitive3d') {
402
+ const geometry = primitiveGeometry(component.primitive);
403
+ this._geometries.add(geometry);
404
+ entity.addComponent(new Mesh3D(geometry, required(this._materials, component.materialId, 'material')));
405
+ return;
406
+ }
407
+ if (component.kind === 'particle3d') {
408
+ const { textureResource, ...descriptor } = component.descriptor;
409
+ const resource = textureResource ? required(this._resources, textureResource, 'particle texture') : undefined;
410
+ const textureSource = resource ? options.resolveTexture?.(resource, null) ?? null : null;
411
+ const emitter = new ParticleEmitter3D({
412
+ ...descriptor,
413
+ ...(textureSource ? { textureSource } : {}),
414
+ ...(this._payload.stateMachine ? { playing: false, emitting: false } : {}),
415
+ });
416
+ entity.addComponent(emitter);
417
+ this._particles.push({
418
+ key: `${node.id}\u0000${component.id}`,
419
+ node,
420
+ componentId: component.id,
421
+ emitter,
422
+ });
423
+ }
424
+ }
425
+
426
+ private _attachHierarchy(): void {
427
+ for (const descriptor of this._payload.nodes) {
428
+ const entity = required(this._entities, descriptor.id, 'node');
429
+ if (descriptor.parent) required(this._entities, descriptor.parent, 'parent node').addChild(entity);
430
+ else this.root.addChild(entity);
431
+ }
432
+ }
433
+
434
+ private async _loadModels(options: HyaAnimation3DRuntimeOptions): Promise<void> {
435
+ const loader = options.loadModel ?? loadGltfModel;
436
+ const tasks: Promise<void>[] = [];
437
+ for (const node of this._payload.nodes) {
438
+ for (const component of node.components) {
439
+ if (component.kind !== 'model3d') continue;
440
+ tasks.push((async () => {
441
+ const resource = required(this._resources, component.resource, 'model resource');
442
+ const model = await loader(resource.uri, { signal: this._abortController.signal });
443
+ if (this._state !== 'active') {
444
+ if (!model.root.destroyed) this._disposeModel(model);
445
+ throw abortError('runtime-destroyed-during-model-load');
446
+ }
447
+ const entity = required(this._entities, node.id, 'model node');
448
+ entity.addChild(model.root);
449
+ applyMaterialOverrides(model.root, component.materialOverrides ?? [], this._materials);
450
+ const runtime = new GltfAnimation3DRuntime(model, {
451
+ signal: this._abortController.signal,
452
+ clipIdPrefix: `hya:${component.id}`,
453
+ });
454
+ this._models.push({ nodeId: node.id, componentId: component.id, model, runtime });
455
+ })());
456
+ }
457
+ }
458
+ await Promise.all(tasks);
459
+ }
460
+
461
+ private _buildClipPartitions(): void {
462
+ for (const clip of this._clips) {
463
+ const mainTracks: Animation3DTrack[] = [];
464
+ const modelTracks = new Map<ModelState, Animation3DTrack[]>();
465
+ for (const track of clip.tracks) {
466
+ const endpoint = this._createSceneEndpoint(track.binding);
467
+ if (endpoint) {
468
+ this._resolver.add(endpoint);
469
+ mainTracks.push(track);
470
+ continue;
471
+ }
472
+ const modelMatch = this._matchModelBinding(track);
473
+ if (modelMatch) {
474
+ let tracks = modelTracks.get(modelMatch.model);
475
+ if (!tracks) { tracks = []; modelTracks.set(modelMatch.model, tracks); }
476
+ tracks.push({ ...track, binding: modelMatch.binding });
477
+ let bindingIds = this._modelBindingIds.get(modelMatch.model);
478
+ if (!bindingIds) {
479
+ bindingIds = new Map();
480
+ this._modelBindingIds.set(modelMatch.model, bindingIds);
481
+ }
482
+ bindingIds.set(track.binding.id, modelMatch.binding.id);
483
+ continue;
484
+ }
485
+ throw new RangeError(`HYA Animation3D binding "${track.binding.id}" cannot be resolved.`);
486
+ }
487
+ if (mainTracks.length > 0 || this._payload.stateMachine) {
488
+ this._mainClips.set(clip.id, { ...clip, tracks: Object.freeze(mainTracks) });
489
+ }
490
+ const byModel = new Map<ModelState, Animation3DClip>();
491
+ for (const [model, tracks] of modelTracks) byModel.set(model, { ...clip, id: `${clip.id}:${model.componentId}`, tracks: Object.freeze(tracks) });
492
+ this._modelClips.set(clip.id, byModel);
493
+ }
494
+ }
495
+
496
+ private _createStateMachineRuntime(): void {
497
+ const definition = this._payload.stateMachine;
498
+ if (!definition) return;
499
+ const partitions: HyaAnimation3DStateMachinePartition[] = [{
500
+ id: 'scene',
501
+ mixer: animation3DMixerRuntime(this.mixer),
502
+ clips: this._mainClips,
503
+ pose: this.pose,
504
+ apply: pose => this._poseApplier.apply(pose),
505
+ }];
506
+ for (const model of this._models) {
507
+ const clips = new Map<string, Animation3DClip>();
508
+ for (const [clipId, byModel] of this._modelClips) {
509
+ const clip = byModel.get(model);
510
+ if (clip) clips.set(clipId, clip);
511
+ }
512
+ if (clips.size === 0) continue;
513
+ partitions.push({
514
+ id: `model:${model.nodeId}:${model.componentId}`,
515
+ mixer: animation3DMixerRuntime(model.runtime.mixer),
516
+ clips,
517
+ ...(this._modelBindingIds.get(model) ? { bindingIds: this._modelBindingIds.get(model)! } : {}),
518
+ pose: model.runtime.pose,
519
+ apply: pose => model.runtime.applySynchronizedPose(pose),
520
+ });
521
+ }
522
+ const particleCues = new Map<string, readonly HyaAnimation3DParticleCue[]>();
523
+ for (const clip of this._clips) {
524
+ const cues: HyaAnimation3DParticleCue[] = [];
525
+ for (const particle of this._particles) {
526
+ const start = Math.min(clip.duration, Math.max(0, particle.node.start ?? 0));
527
+ const authoredEnd = particle.node.duration === undefined
528
+ ? clip.duration
529
+ : (particle.node.start ?? 0) + particle.node.duration;
530
+ const end = Math.min(clip.duration, Math.max(0, authoredEnd));
531
+ if (end <= start) continue;
532
+ cues.push(Object.freeze({
533
+ key: particle.key,
534
+ emitter: particle.emitter,
535
+ start,
536
+ end,
537
+ }));
538
+ }
539
+ particleCues.set(clip.id, Object.freeze(cues));
540
+ }
541
+ this._stateMachine = new HyaAnimation3DStateMachineRuntime({
542
+ definition,
543
+ clips: this._clips,
544
+ partitions,
545
+ particleCues,
546
+ });
547
+ }
548
+
549
+ private _createSceneEndpoint(binding: Animation3DBinding): HyaAnimation3DResolvedEndpoint | null {
550
+ if (binding.path === 'morph.weights') return null;
551
+ const nodeId = binding.target.kind === 'node-id' ? binding.target.nodeId
552
+ : binding.target.kind === 'node-path' && binding.target.segments.length === 1 ? binding.target.segments[0]
553
+ : null;
554
+ if (binding.path.startsWith('transform.') && nodeId) {
555
+ const state = this._transforms.get(nodeId);
556
+ if (!state) return null;
557
+ const target = binding.path === 'transform.translation' ? state.translation
558
+ : binding.path === 'transform.rotation' ? state.rotation : state.scale;
559
+ return endpoint(binding, new Float32Array(target), value => { copy(value, target, binding.valueSize); commitTransform(state); });
560
+ }
561
+ if (binding.path !== 'property') return null;
562
+ if (binding.component === 'material3d' && binding.target.kind === 'slot') {
563
+ const material = this._materials.get(binding.target.slot);
564
+ if (!material) return null;
565
+ return materialEndpoint(binding, material);
566
+ }
567
+ if (binding.component === 'camera3d' && nodeId) {
568
+ const camera = this._cameras.get(nodeId);
569
+ if (!camera) return null;
570
+ return cameraEndpoint(binding, camera, this._payload.viewport.width / this._payload.viewport.height);
571
+ }
572
+ return null;
573
+ }
574
+
575
+ private _matchModelBinding(track: Animation3DTrack): { model: ModelState; binding: Animation3DBinding } | null {
576
+ const candidates: Array<{ model: ModelState; binding: Animation3DBinding }> = [];
577
+ for (const model of this._models) {
578
+ for (const sourceClip of model.runtime.clips) {
579
+ for (const sourceTrack of sourceClip.tracks) {
580
+ if (sameModelBinding(track.binding, sourceTrack.binding, model.nodeId)) candidates.push({ model, binding: sourceTrack.binding });
581
+ }
582
+ }
583
+ }
584
+ const exact = candidates.find(candidate => candidate.binding.id === track.binding.id);
585
+ if (exact) return exact;
586
+ return candidates.length === 1 ? candidates[0]! : null;
587
+ }
588
+
589
+ private _addPreviewLights(): void {
590
+ const ambient = new Entity('HYA Preview Ambient');
591
+ ambient.addComponent(new AmbientLight({ color: [0.72, 0.8, 1], intensity: 0.5 }));
592
+ this.root.addChild(ambient);
593
+ const sun = new Entity('HYA Preview Sun');
594
+ sun.addComponent(new DirectionalLight({ color: [1, 0.94, 0.84], intensity: 2, direction: [-0.4, -1, -0.35], castShadow: false }));
595
+ this.root.addChild(sun);
596
+ }
597
+
598
+ private _assertActive(): void {
599
+ if (this._state !== 'active' || this.root.destroyed) {
600
+ this.destroy();
601
+ throw new Error('HYA Animation3D runtime is destroyed.');
602
+ }
603
+ }
604
+ }
605
+
606
+ export async function createHyaAnimation3DRuntime(options: HyaAnimation3DRuntimeOptions): Promise<HyaAnimation3DRuntime> {
607
+ return await HyaAnimation3DRuntime.create(options);
608
+ }
609
+
610
+ function lowerRuntimeClip(value: HyaAnimation3DPayload['clips'][number]): Animation3DClip {
611
+ return Object.freeze({
612
+ format: 'haiyue-animation3d-clip@1',
613
+ id: value.id,
614
+ name: value.name,
615
+ duration: value.duration,
616
+ tracks: Object.freeze(value.tracks.map(track => Object.freeze({
617
+ id: track.id,
618
+ binding: track.binding,
619
+ interpolation: track.interpolation,
620
+ times: new Float32Array(Array.from(track.times)),
621
+ values: new Float32Array(Array.from(track.values)),
622
+ }))),
623
+ events: Object.freeze([...value.events]),
624
+ });
625
+ }
626
+
627
+ function materialFromDescriptor(value: HyaAnimation3DMaterialDescriptor): PbrMaterial {
628
+ return new PbrMaterial({
629
+ baseColor: value.baseColorFactor,
630
+ metallic: value.metallicFactor,
631
+ roughness: Math.max(0.04, value.roughnessFactor),
632
+ emissiveFactor: value.emissiveFactor,
633
+ alphaMode: value.alphaMode,
634
+ alphaCutoff: value.alphaCutoff ?? 0.5,
635
+ doubleSided: value.doubleSided,
636
+ });
637
+ }
638
+
639
+ function cameraFromProjection(value: HyaAnimation3DCameraProjection, aspect: number): Camera3D {
640
+ if (value.kind === 'perspective') return new Camera3D({ type: 'perspective', fov: value.fovYRadians, near: value.near, far: value.far, aspect });
641
+ const half = value.orthoHeight * 0.5;
642
+ return new Camera3D({ type: 'orthographic', near: value.near, far: value.far, aspect, left: -half * aspect, right: half * aspect, top: half, bottom: -half });
643
+ }
644
+
645
+ function primitiveGeometry(value: Extract<HyaAnimation3DComponentDescriptor, { kind: 'primitive3d' }>['primitive']): Geometry3D {
646
+ switch (value) {
647
+ case 'box': return createBox3D();
648
+ case 'sphere': return createSphere3D();
649
+ case 'plane': return createPlane3D();
650
+ case 'cylinder': return createCylinder3D();
651
+ case 'cone': return createCone3D();
652
+ }
653
+ }
654
+
655
+ function commitTransform(state: TransformState): void {
656
+ composeTrsMatrix(state.translation, state.rotation, state.scale, state.matrix as Float32Array<ArrayBuffer>);
657
+ state.transform.localMatrix = state.matrix;
658
+ }
659
+
660
+ function endpoint(binding: Animation3DBinding, base: Float32Array, write: (value: ArrayLike<number>) => void): HyaAnimation3DResolvedEndpoint {
661
+ return new HyaAnimation3DResolvedEndpoint(binding, out => copy(base, out, binding.valueSize), write);
662
+ }
663
+
664
+ function materialEndpoint(binding: Extract<Animation3DBinding, { path: 'property' }>, material: PbrMaterial): HyaAnimation3DResolvedEndpoint {
665
+ const base = new Float32Array(binding.valueSize);
666
+ if (binding.property === 'baseColorFactor') material.baseColor.writeLinear(base);
667
+ else {
668
+ const value: ArrayLike<number> = binding.property === 'emissiveFactor' ? material.emissiveFactor
669
+ : [binding.property === 'metallicFactor' ? material.metallic
670
+ : binding.property === 'roughnessFactor' ? material.roughness
671
+ : material.alphaCutoff];
672
+ copy(value, base, binding.valueSize);
673
+ }
674
+ const write = (value: ArrayLike<number>): void => {
675
+ if (binding.property === 'baseColorFactor') material.baseColor = tuple4(value);
676
+ else if (binding.property === 'emissiveFactor') material.emissiveFactor = tuple3(value);
677
+ else if (binding.property === 'metallicFactor') material.metallic = value[0] ?? 0;
678
+ else if (binding.property === 'roughnessFactor') material.roughness = Math.max(0.04, value[0] ?? 0.04);
679
+ else if (binding.property === 'alphaCutoff') material.alphaCutoff = value[0] ?? 0.5;
680
+ };
681
+ return endpoint(binding, base, write);
682
+ }
683
+
684
+ function cameraEndpoint(binding: Extract<Animation3DBinding, { path: 'property' }>, camera: Camera3D, aspect: number): HyaAnimation3DResolvedEndpoint {
685
+ const base = new Float32Array([binding.property === 'fovYRadians' ? camera.fov
686
+ : binding.property === 'near' ? camera.near
687
+ : binding.property === 'far' ? camera.far
688
+ : camera.orthoTop - camera.orthoBottom]);
689
+ const write = (value: ArrayLike<number>): void => {
690
+ const next = value[0] ?? 0;
691
+ if (binding.property === 'fovYRadians') camera.fov = next;
692
+ else if (binding.property === 'near') camera.near = next;
693
+ else if (binding.property === 'far') camera.far = next;
694
+ else if (binding.property === 'orthoHeight') {
695
+ const half = next * 0.5;
696
+ camera.orthoLeft = -half * aspect; camera.orthoRight = half * aspect;
697
+ camera.orthoTop = half; camera.orthoBottom = -half;
698
+ }
699
+ };
700
+ return endpoint(binding, base, write);
701
+ }
702
+
703
+ function sameModelBinding(candidate: Animation3DBinding, endpoint: Animation3DBinding, modelNodeId: string): boolean {
704
+ if (candidate.id === endpoint.id) return candidate.path === endpoint.path && candidate.valueSize === endpoint.valueSize;
705
+ if (candidate.path !== endpoint.path || candidate.valueSize !== endpoint.valueSize) return false;
706
+ if (candidate.target.kind === 'node-path' && endpoint.target.kind === 'node-path') {
707
+ const authored = candidate.target.segments[0] === modelNodeId ? candidate.target.segments.slice(1) : candidate.target.segments;
708
+ const endpointSegments = endpoint.target.segments;
709
+ return authored.length <= endpointSegments.length
710
+ && authored.every((segment, index) => segment === endpointSegments[endpointSegments.length - authored.length + index]);
711
+ }
712
+ if (candidate.path === 'morph.weights' && candidate.target.kind === 'slot') {
713
+ return candidate.target.slot === modelNodeId || candidate.target.slot.startsWith(`${modelNodeId}:`);
714
+ }
715
+ return false;
716
+ }
717
+
718
+ function applyMaterialOverrides(root: Entity, overrides: readonly Readonly<{ slot: string; materialId: string }>[], materials: ReadonlyMap<string, PbrMaterial>): void {
719
+ if (overrides.length === 0) return;
720
+ const bySlot = new Map(overrides.map(item => [item.slot, required(materials, item.materialId, 'material override')]));
721
+ const pending = [root];
722
+ while (pending.length > 0) {
723
+ const entity = pending.pop()!;
724
+ const mesh = entity.getComponent(Mesh3D);
725
+ const material = bySlot.get(entity.name);
726
+ if (mesh && material) mesh.material = material;
727
+ pending.push(...entity.children);
728
+ }
729
+ }
730
+
731
+ function copy(source: ArrayLike<number>, target: Float32Array, count: number): void {
732
+ for (let index = 0; index < count; index++) target[index] = source[index] ?? 0;
733
+ }
734
+
735
+ function tuple3(value: ArrayLike<number>): [number, number, number] {
736
+ return [value[0] ?? 0, value[1] ?? 0, value[2] ?? 0];
737
+ }
738
+
739
+ function tuple4(value: ArrayLike<number>): [number, number, number, number] {
740
+ return [value[0] ?? 0, value[1] ?? 0, value[2] ?? 0, value[3] ?? 0];
741
+ }
742
+
743
+ function withoutGroupOptions(options: HyaAnimation3DPlayOptions): import('../Animation3DAction').Animation3DActionOptions {
744
+ const { fadeFrom: _fadeFrom, fadeDuration: _fadeDuration, warp: _warp, id: _id, ...result } = options;
745
+ return result;
746
+ }
747
+
748
+ function required<Key, Value>(map: ReadonlyMap<Key, Value>, key: Key, label: string): Value {
749
+ const value = map.get(key);
750
+ if (!value) throw new RangeError(`Unknown ${label} "${String(key)}".`);
751
+ return value;
752
+ }
753
+
754
+ function abortError(reason: unknown): DOMException {
755
+ return new DOMException(reason === undefined ? 'The operation was aborted.' : String(reason), 'AbortError');
756
+ }