@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,521 @@
1
+ import { EngineError, EngineErrorCode } from '@haiyue/engine';
2
+ import { ErrorDomain, ErrorRecovery } from '@haiyue/engine/core';
3
+
4
+ export interface BoneData {
5
+ name: string;
6
+ parent?: string;
7
+ inherit?: string;
8
+ x: number;
9
+ y: number;
10
+ rotation: number;
11
+ shearX: number;
12
+ shearY: number;
13
+ scaleX: number;
14
+ scaleY: number;
15
+ length: number;
16
+ }
17
+
18
+ export interface IkConstraintData {
19
+ name: string;
20
+ order: number;
21
+ skin: boolean;
22
+ bones: string[];
23
+ target: string;
24
+ mix: number;
25
+ softness: number;
26
+ bendPositive: boolean;
27
+ }
28
+
29
+ export interface TransformConstraintData {
30
+ name: string;
31
+ order: number;
32
+ skin: boolean;
33
+ bones: string[];
34
+ target: string;
35
+ rotation: number;
36
+ x: number;
37
+ y: number;
38
+ scaleX: number;
39
+ scaleY: number;
40
+ shearY: number;
41
+ mixRotate: number;
42
+ mixX: number;
43
+ mixY: number;
44
+ mixScaleX: number;
45
+ mixScaleY: number;
46
+ mixShearY: number;
47
+ localSource: boolean;
48
+ localTarget: boolean;
49
+ additive: boolean;
50
+ }
51
+
52
+ export interface PathConstraintData {
53
+ name: string;
54
+ order: number;
55
+ skin: boolean;
56
+ bones: string[];
57
+ slot: string;
58
+ position: number;
59
+ spacing: number;
60
+ mixRotate: number;
61
+ mixX: number;
62
+ mixY: number;
63
+ positionMode: string;
64
+ spacingMode: string;
65
+ rotateMode: string;
66
+ offsetRotation: number;
67
+ }
68
+
69
+ export interface SliderConstraintData {
70
+ name: string;
71
+ order: number;
72
+ skin: boolean;
73
+ animation: string;
74
+ bone?: string;
75
+ property: string;
76
+ scale: number;
77
+ offset: number;
78
+ loop: boolean;
79
+ local: boolean;
80
+ mix: number;
81
+ }
82
+
83
+ export interface SlotData {
84
+ name: string;
85
+ bone: string;
86
+ attachment?: string;
87
+ color?: [number, number, number, number];
88
+ blend: 'normal' | 'additive' | string;
89
+ }
90
+
91
+ export interface RegionAttachment {
92
+ type?: string;
93
+ name: string;
94
+ path: string;
95
+ x: number;
96
+ y: number;
97
+ rotation: number;
98
+ scaleX: number;
99
+ scaleY: number;
100
+ width: number;
101
+ height: number;
102
+ color?: [number, number, number, number];
103
+ uvs?: number[];
104
+ triangles?: number[];
105
+ vertices?: number[];
106
+ vertexCount?: number;
107
+ lengths?: number[];
108
+ sequence?: { count?: number; start?: number; digits?: number; setup?: number };
109
+ closed?: boolean;
110
+ constantSpeed?: boolean;
111
+ end?: string;
112
+ skin?: string;
113
+ source?: string;
114
+ }
115
+
116
+ export interface SpineAnimationBoneTimelines {
117
+ rotate?: unknown[];
118
+ translate?: unknown[];
119
+ scale?: unknown[];
120
+ shear?: unknown[];
121
+ [timeline: string]: unknown;
122
+ }
123
+
124
+ export interface SpineAnimationData {
125
+ bones?: Record<string, SpineAnimationBoneTimelines>;
126
+ slots?: Record<string, {
127
+ attachment?: Array<{ time?: number; name?: string | null }>;
128
+ rgba?: unknown[];
129
+ [timeline: string]: unknown;
130
+ }>;
131
+ attachments?: Record<string, Record<string, Record<string, { sequence?: unknown[] }>>>;
132
+ ik?: Record<string, unknown[]>;
133
+ transform?: Record<string, unknown[]>;
134
+ path?: Record<string, unknown[]>;
135
+ slider?: Record<string, { mix?: unknown[] }>;
136
+ drawOrder?: Array<{ time?: number; offsets?: Array<{ slot: string; offset?: number }> }>;
137
+ [timeline: string]: unknown;
138
+ }
139
+
140
+ export interface SpineData {
141
+ bones: BoneData[];
142
+ ik: IkConstraintData[];
143
+ transform: TransformConstraintData[];
144
+ path: PathConstraintData[];
145
+ sliders: SliderConstraintData[];
146
+ slots: SlotData[];
147
+ skins: Record<string, Record<string, Record<string, RegionAttachment>>>;
148
+ skinConstraints: Record<string, { ik: string[]; transform: string[]; path: string[]; slider: string[] }>;
149
+ animations: Record<string, SpineAnimationData>;
150
+ }
151
+
152
+ export interface BonePose {
153
+ data: BoneData;
154
+ a: number;
155
+ b: number;
156
+ c: number;
157
+ d: number;
158
+ worldX: number;
159
+ worldY: number;
160
+ rotation: number;
161
+ shearX: number;
162
+ shearY: number;
163
+ x: number;
164
+ y: number;
165
+ scaleX: number;
166
+ scaleY: number;
167
+ }
168
+
169
+ export interface SliderAnimationState {
170
+ animation: string;
171
+ time: number;
172
+ loop: boolean;
173
+ mix: number;
174
+ }
175
+
176
+ export function normalizeSpineData(input: unknown): SpineData {
177
+ if (typeof input !== 'object' || input === null || Array.isArray(input)) {
178
+ throw invalidSpineData('Spine JSON root must be an object.', 'spine');
179
+ }
180
+ const record = input as Record<string, unknown>;
181
+ if (!Array.isArray(record.bones)) throw invalidSpineData('Spine JSON bones must be an array.', 'spine.bones');
182
+ if (!Array.isArray(record.slots)) throw invalidSpineData('Spine JSON slots must be an array.', 'spine.slots');
183
+ for (const [index, bone] of record.bones.entries()) {
184
+ if (typeof bone !== 'object' || bone === null || typeof (bone as Record<string, unknown>).name !== 'string') {
185
+ throw invalidSpineData(`Spine bone ${index} must include a string name.`, `spine.bones[${index}].name`);
186
+ }
187
+ }
188
+ for (const [index, slot] of record.slots.entries()) {
189
+ if (typeof slot !== 'object' || slot === null || typeof (slot as Record<string, unknown>).name !== 'string') {
190
+ throw invalidSpineData(`Spine slot ${index} must include a string name.`, `spine.slots[${index}].name`);
191
+ }
192
+ }
193
+ return normalizeValidatedSpineData(record as unknown as SpineSourceDocument);
194
+ }
195
+
196
+ // Spine JSON is an open, version-dependent third-party schema. Dynamic access is
197
+ // intentionally confined to this normalization adapter; domain code consumes SpineData.
198
+ function normalizeValidatedSpineData(input: SpineSourceDocument): SpineData {
199
+ const skins: SpineData['skins'] = {};
200
+ const skinConstraints: SpineData['skinConstraints'] = {};
201
+ if (Array.isArray(input.skins)) {
202
+ for (const skin of input.skins) {
203
+ const name = skin.name || 'default';
204
+ skins[name] = normalizeSkinAttachments(skin.attachments);
205
+ skinConstraints[name] = {
206
+ ik: skin.ik ?? [],
207
+ transform: skin.transform ?? [],
208
+ path: skin.path ?? [],
209
+ slider: skin.slider ?? skin.sliders ?? [],
210
+ };
211
+ }
212
+ } else {
213
+ for (const [name, attachments] of Object.entries(input.skins ?? {})) {
214
+ skins[name] = normalizeSkinAttachments(attachments);
215
+ }
216
+ for (const name of Object.keys(skins)) skinConstraints[name] = { ik: [], transform: [], path: [], slider: [] };
217
+ }
218
+ resolveLinkedMeshes(skins);
219
+ const constraints = Array.isArray(input.constraints)
220
+ ? input.constraints.map((constraint, index) => ({ ...constraint, __order: index }))
221
+ : null;
222
+ const ikConstraints = constraints
223
+ ? constraints.filter(constraint => constraint.type === 'ik')
224
+ : (input.ik ?? []);
225
+ const transformConstraints = constraints
226
+ ? constraints.filter(constraint => constraint.type === 'transform')
227
+ : (input.transform ?? []);
228
+ const pathConstraints = constraints
229
+ ? constraints.filter(constraint => constraint.type === 'path')
230
+ : (input.path ?? []);
231
+ const sliderConstraints = constraints
232
+ ? constraints.filter(constraint => constraint.type === 'slider')
233
+ : (input.sliders ?? input.slider ?? []);
234
+ return {
235
+ bones: (input.bones ?? []).map(bone => ({
236
+ name: bone.name,
237
+ ...(bone.parent === undefined ? {} : { parent: bone.parent }),
238
+ ...(bone.inherit === undefined ? {} : { inherit: bone.inherit }),
239
+ x: bone.x ?? 0,
240
+ y: bone.y ?? 0,
241
+ rotation: bone.rotation ?? 0,
242
+ shearX: bone.shearX ?? 0,
243
+ shearY: bone.shearY ?? 0,
244
+ scaleX: bone.scaleX ?? 1,
245
+ scaleY: bone.scaleY ?? 1,
246
+ length: bone.length ?? 0,
247
+ })),
248
+ ik: ikConstraints.map((constraint, index) => ({
249
+ name: constraint.name,
250
+ order: constraint.order ?? constraint.__order ?? index,
251
+ skin: constraint.skin ?? false,
252
+ bones: constraint.bones ?? [],
253
+ target: constraint.target ?? constraint.source ?? '',
254
+ mix: constraint.mix ?? 1,
255
+ softness: constraint.softness ?? 0,
256
+ bendPositive: constraint.bendPositive ?? true,
257
+ })),
258
+ transform: transformConstraints.map((constraint, index) => ({
259
+ name: constraint.name,
260
+ order: constraint.order ?? constraint.__order ?? index,
261
+ skin: constraint.skin ?? false,
262
+ bones: constraint.bones ?? [],
263
+ target: constraint.target ?? constraint.source ?? '',
264
+ rotation: constraint.rotation ?? 0,
265
+ x: constraint.x ?? 0,
266
+ y: constraint.y ?? 0,
267
+ scaleX: constraint.scaleX ?? 0,
268
+ scaleY: constraint.scaleY ?? 0,
269
+ shearY: constraint.shearY ?? 0,
270
+ ...normalizeTransformMixes(constraint),
271
+ localSource: constraint.localSource ?? false,
272
+ localTarget: constraint.localTarget ?? false,
273
+ additive: constraint.additive ?? false,
274
+ })),
275
+ path: pathConstraints.map((constraint, index) => ({
276
+ name: constraint.name,
277
+ order: constraint.order ?? constraint.__order ?? index,
278
+ skin: constraint.skin ?? false,
279
+ bones: constraint.bones ?? [],
280
+ slot: constraint.slot ?? '',
281
+ position: constraint.position ?? 0,
282
+ spacing: constraint.spacing ?? 0,
283
+ mixRotate: constraint.mixRotate ?? 1,
284
+ mixX: constraint.mixX ?? 1,
285
+ mixY: constraint.mixY ?? constraint.mixX ?? 1,
286
+ positionMode: constraint.positionMode ?? 'percent',
287
+ spacingMode: constraint.spacingMode ?? 'length',
288
+ rotateMode: constraint.rotateMode ?? 'tangent',
289
+ offsetRotation: constraint.rotation ?? 0,
290
+ })),
291
+ sliders: sliderConstraints.map((constraint, index) => ({
292
+ name: constraint.name,
293
+ order: constraint.order ?? constraint.__order ?? index,
294
+ skin: constraint.skin ?? false,
295
+ animation: constraint.animation ?? '',
296
+ ...(constraint.bone === undefined ? {} : { bone: constraint.bone }),
297
+ property: constraint.property ?? 'rotate',
298
+ scale: constraint.scale ?? 1,
299
+ offset: constraint.offset ?? 0,
300
+ loop: constraint.loop ?? false,
301
+ local: constraint.local ?? false,
302
+ mix: constraint.mix ?? 1,
303
+ })),
304
+ slots: (input.slots ?? []).map(slot => {
305
+ const color = parseColor(slot.color);
306
+ return {
307
+ name: slot.name,
308
+ bone: slot.bone,
309
+ ...(slot.attachment === undefined ? {} : { attachment: slot.attachment }),
310
+ ...(color === undefined ? {} : { color }),
311
+ blend: slot.blend ?? 'normal',
312
+ };
313
+ }),
314
+ skins,
315
+ skinConstraints,
316
+ animations: input.animations ?? {},
317
+ };
318
+ }
319
+
320
+ function invalidSpineData(message: string, path: string): EngineError {
321
+ return new EngineError(EngineErrorCode.AssetInvalidData, message, {
322
+ domain: ErrorDomain.Component,
323
+ recovery: ErrorRecovery.ReleaseResource,
324
+ context: { resourceType: 'skeleton/spine' },
325
+ path,
326
+ });
327
+ }
328
+
329
+ export function parseColor(value?: string): [number, number, number, number] | undefined {
330
+ if (!value || value.length < 8) return undefined;
331
+ return [
332
+ parseInt(value.slice(0, 2), 16) / 255,
333
+ parseInt(value.slice(2, 4), 16) / 255,
334
+ parseInt(value.slice(4, 6), 16) / 255,
335
+ parseInt(value.slice(6, 8), 16) / 255,
336
+ ];
337
+ }
338
+
339
+ function resolveLinkedMeshes(skins: SpineData['skins']): void {
340
+ for (const [skinName, skin] of Object.entries(skins)) {
341
+ for (const [slotName, attachments] of Object.entries(skin)) {
342
+ for (const [attachmentName, attachment] of Object.entries(attachments)) {
343
+ if (attachment.type !== 'linkedmesh') continue;
344
+ const sourceSkinName = attachment.skin ?? skinName;
345
+ const sourceName = attachment.source ?? attachmentName;
346
+ const source = skins[sourceSkinName]?.[slotName]?.[sourceName] ?? skins.default?.[slotName]?.[sourceName];
347
+ if (!source) continue;
348
+ attachments[attachmentName] = {
349
+ ...source,
350
+ ...attachment,
351
+ type: source.type ?? 'mesh',
352
+ ...(source.uvs === undefined ? {} : { uvs: source.uvs }),
353
+ ...(source.triangles === undefined ? {} : { triangles: source.triangles }),
354
+ ...(source.vertices === undefined ? {} : { vertices: source.vertices }),
355
+ ...(source.vertexCount === undefined ? {} : { vertexCount: source.vertexCount }),
356
+ };
357
+ }
358
+ }
359
+ }
360
+ }
361
+
362
+ function normalizeSkinAttachments(attachments: SpineSourceSkinAttachments | undefined): Record<string, Record<string, RegionAttachment>> {
363
+ const normalized: Record<string, Record<string, RegionAttachment>> = {};
364
+ for (const [slotName, slotAttachments] of Object.entries(attachments ?? {})) {
365
+ normalized[slotName] = {};
366
+ for (const [attachmentName, attachment] of Object.entries(slotAttachments ?? {})) {
367
+ const { color: rawColor, ...attachmentFields } = attachment;
368
+ const color = typeof rawColor === 'string' ? parseColor(rawColor) : rawColor;
369
+ normalized[slotName][attachmentName] = {
370
+ ...attachmentFields,
371
+ name: attachment.name ?? attachmentName,
372
+ path: attachment.path ?? attachment.name ?? attachmentName,
373
+ x: attachment.x ?? 0,
374
+ y: attachment.y ?? 0,
375
+ rotation: attachment.rotation ?? 0,
376
+ scaleX: attachment.scaleX ?? 1,
377
+ scaleY: attachment.scaleY ?? 1,
378
+ width: attachment.width ?? 0,
379
+ height: attachment.height ?? 0,
380
+ ...(color === undefined ? {} : { color }),
381
+ };
382
+ }
383
+ }
384
+ return normalized;
385
+ }
386
+
387
+ function normalizeTransformMixes(constraint: SpineSourceConstraint): Pick<
388
+ TransformConstraintData,
389
+ 'mixRotate' | 'mixX' | 'mixY' | 'mixScaleX' | 'mixScaleY' | 'mixShearY'
390
+ > {
391
+ const targets = getTransformPropertyTargets(constraint.properties);
392
+ if (!targets) {
393
+ return {
394
+ mixRotate: constraint.mixRotate ?? 1,
395
+ mixX: constraint.mixX ?? 1,
396
+ mixY: constraint.mixY ?? constraint.mixX ?? 1,
397
+ mixScaleX: constraint.mixScaleX ?? 1,
398
+ mixScaleY: constraint.mixScaleY ?? constraint.mixScaleX ?? 1,
399
+ mixShearY: constraint.mixShearY ?? 1,
400
+ };
401
+ }
402
+ return {
403
+ mixRotate: targets.rotate ? constraint.mixRotate ?? 1 : 0,
404
+ mixX: targets.x ? constraint.mixX ?? 1 : 0,
405
+ mixY: targets.y ? constraint.mixY ?? constraint.mixX ?? 1 : 0,
406
+ mixScaleX: targets.scaleX ? constraint.mixScaleX ?? 1 : 0,
407
+ mixScaleY: targets.scaleY ? constraint.mixScaleY ?? constraint.mixScaleX ?? 1 : 0,
408
+ mixShearY: targets.shearY ? constraint.mixShearY ?? 1 : 0,
409
+ };
410
+ }
411
+
412
+ function getTransformPropertyTargets(properties: unknown): Record<'rotate' | 'x' | 'y' | 'scaleX' | 'scaleY' | 'shearY', boolean> | null {
413
+ if (!properties || typeof properties !== 'object') return null;
414
+ const targets = { rotate: false, x: false, y: false, scaleX: false, scaleY: false, shearY: false };
415
+ let hasTarget = false;
416
+ for (const from of Object.values(properties)) {
417
+ if (!from || typeof from !== 'object') continue;
418
+ const to = (from as Record<string, unknown>).to;
419
+ if (!to || typeof to !== 'object') continue;
420
+ for (const name of Object.keys(to)) {
421
+ if (name in targets) {
422
+ targets[name as keyof typeof targets] = true;
423
+ hasTarget = true;
424
+ }
425
+ }
426
+ }
427
+ return hasTarget ? targets : null;
428
+ }
429
+
430
+ type SpineSourceAttachment = Partial<Omit<RegionAttachment, 'color'>> & {
431
+ color?: string | [number, number, number, number];
432
+ };
433
+ type SpineSourceSkinAttachments = Record<string, Record<string, SpineSourceAttachment>>;
434
+
435
+ interface SpineSourceSkin {
436
+ name?: string;
437
+ attachments?: SpineSourceSkinAttachments;
438
+ ik?: string[];
439
+ transform?: string[];
440
+ path?: string[];
441
+ slider?: string[];
442
+ sliders?: string[];
443
+ }
444
+
445
+ interface SpineSourceConstraint {
446
+ type?: 'ik' | 'transform' | 'path' | 'slider' | string;
447
+ __order?: number;
448
+ name: string;
449
+ order?: number;
450
+ skin?: boolean;
451
+ bones?: string[];
452
+ target?: string;
453
+ source?: string;
454
+ mix?: number;
455
+ softness?: number;
456
+ bendPositive?: boolean;
457
+ rotation?: number;
458
+ x?: number;
459
+ y?: number;
460
+ scaleX?: number;
461
+ scaleY?: number;
462
+ shearY?: number;
463
+ mixRotate?: number;
464
+ mixX?: number;
465
+ mixY?: number;
466
+ mixScaleX?: number;
467
+ mixScaleY?: number;
468
+ mixShearY?: number;
469
+ localSource?: boolean;
470
+ localTarget?: boolean;
471
+ additive?: boolean;
472
+ properties?: unknown;
473
+ slot?: string;
474
+ position?: number;
475
+ spacing?: number;
476
+ positionMode?: string;
477
+ spacingMode?: string;
478
+ rotateMode?: string;
479
+ animation?: string;
480
+ bone?: string;
481
+ property?: string;
482
+ scale?: number;
483
+ offset?: number;
484
+ loop?: boolean;
485
+ local?: boolean;
486
+ }
487
+
488
+ interface SpineSourceBone {
489
+ name: string;
490
+ parent?: string;
491
+ inherit?: string;
492
+ x?: number;
493
+ y?: number;
494
+ rotation?: number;
495
+ shearX?: number;
496
+ shearY?: number;
497
+ scaleX?: number;
498
+ scaleY?: number;
499
+ length?: number;
500
+ }
501
+
502
+ interface SpineSourceSlot {
503
+ name: string;
504
+ bone: string;
505
+ attachment?: string;
506
+ color?: string;
507
+ blend?: string;
508
+ }
509
+
510
+ interface SpineSourceDocument {
511
+ bones?: SpineSourceBone[];
512
+ slots?: SpineSourceSlot[];
513
+ skins?: SpineSourceSkin[] | Record<string, SpineSourceSkinAttachments>;
514
+ constraints?: SpineSourceConstraint[];
515
+ ik?: SpineSourceConstraint[];
516
+ transform?: SpineSourceConstraint[];
517
+ path?: SpineSourceConstraint[];
518
+ sliders?: SpineSourceConstraint[];
519
+ slider?: SpineSourceConstraint[];
520
+ animations?: Record<string, SpineAnimationData>;
521
+ }
@@ -0,0 +1,195 @@
1
+ import type { Spine2DComponent } from './Spine2DComponent';
2
+ import type {
3
+ RegionAttachment,
4
+ SliderAnimationState,
5
+ SlotData,
6
+ SpineData,
7
+ } from './SpineSkeletonRuntime';
8
+ import {
9
+ findCompiledFrameIndex,
10
+ sampleCompiledColor,
11
+ type SpineTimelineSamplerState,
12
+ } from './SpineTimelineRuntime';
13
+
14
+ interface SpineSlotAnimationRuntime {
15
+ readonly data: SpineData;
16
+ readonly timelineSamplerState: SpineTimelineSamplerState;
17
+ readonly slotColorScratch: [number, number, number, number];
18
+ readonly sliderColorScratch: [number, number, number, number];
19
+ }
20
+
21
+ type SpineSlotAnimationComponent = Pick<Spine2DComponent, 'animation' | 'skin'>;
22
+
23
+ interface SpineSequenceFrame {
24
+ readonly time?: number;
25
+ readonly delay?: number;
26
+ readonly index?: number;
27
+ readonly mode?: string;
28
+ }
29
+
30
+ const WHITE_COLOR: [number, number, number, number] = [1, 1, 1, 1];
31
+
32
+ /** Resolves the active slot attachment timeline without owning pose evaluation. */
33
+ export function getSpineSlotAttachmentName(
34
+ runtime: SpineSlotAnimationRuntime,
35
+ component: SpineSlotAnimationComponent,
36
+ slot: SlotData,
37
+ time: number,
38
+ ): string | null {
39
+ let name: string | null = slot.attachment ?? null;
40
+ const frames = runtime.data.animations[component.animation]?.slots?.[slot.name]?.attachment;
41
+ if (!Array.isArray(frames) || frames.length === 0) return name;
42
+ const frameIndex = findCompiledFrameIndex(frames, time, runtime.timelineSamplerState);
43
+ if (frameIndex >= 0) name = frames[frameIndex]?.name ?? null;
44
+ return name;
45
+ }
46
+
47
+ /** Resolves sequence attachments across the base animation and slider overlays. */
48
+ export function getSpineAttachmentRegionName(
49
+ runtime: SpineSlotAnimationRuntime,
50
+ component: SpineSlotAnimationComponent,
51
+ slot: SlotData,
52
+ attachmentName: string,
53
+ attachment: RegionAttachment,
54
+ time: number,
55
+ sliderStates: readonly SliderAnimationState[],
56
+ ): string {
57
+ const basePath = attachment.path || attachment.name || attachmentName;
58
+ const sequence = attachment.sequence;
59
+ if (!sequence?.count) return basePath;
60
+ let index = sampleSequenceIndex(
61
+ getSequenceFrames(runtime.data, component.animation, component.skin, slot.name, attachmentName),
62
+ time,
63
+ sequence,
64
+ runtime.timelineSamplerState,
65
+ );
66
+ for (const slider of sliderStates) {
67
+ const frames = getSequenceFrames(runtime.data, slider.animation, component.skin, slot.name, attachmentName);
68
+ if (Array.isArray(frames) && frames.length > 0) {
69
+ index = sampleSequenceIndex(frames, slider.time, sequence, runtime.timelineSamplerState);
70
+ }
71
+ }
72
+ const frame = String((sequence.start ?? 0) + index).padStart(sequence.digits ?? 0, '0');
73
+ return `${basePath}${frame}`;
74
+ }
75
+
76
+ /** Samples slot RGBA timelines and slider blends into runtime-owned scratch storage. */
77
+ export function getSpineSlotColor(
78
+ runtime: SpineSlotAnimationRuntime,
79
+ component: SpineSlotAnimationComponent,
80
+ slot: SlotData,
81
+ time: number,
82
+ sliderStates: readonly SliderAnimationState[],
83
+ ): [number, number, number, number] {
84
+ const frames = runtime.data.animations[component.animation]?.slots?.[slot.name]?.rgba;
85
+ const fallback = slot.color ?? WHITE_COLOR;
86
+ const color = runtime.slotColorScratch;
87
+ if (Array.isArray(frames) && frames.length > 0) {
88
+ sampleCompiledColor(frames, time, fallback, color, runtime.timelineSamplerState);
89
+ } else {
90
+ copyColor(fallback, color);
91
+ }
92
+ for (const slider of sliderStates) {
93
+ const sliderFrames = runtime.data.animations[slider.animation]?.slots?.[slot.name]?.rgba;
94
+ if (!Array.isArray(sliderFrames) || sliderFrames.length === 0) continue;
95
+ sampleCompiledColor(
96
+ sliderFrames,
97
+ slider.time,
98
+ color,
99
+ runtime.sliderColorScratch,
100
+ runtime.timelineSamplerState,
101
+ );
102
+ mixColorInto(color, runtime.sliderColorScratch, slider.mix);
103
+ }
104
+ return color;
105
+ }
106
+
107
+ function getSequenceFrames(
108
+ data: SpineData,
109
+ animationName: string,
110
+ skinName: string,
111
+ slotName: string,
112
+ attachmentName: string,
113
+ ): SpineSequenceFrame[] | undefined {
114
+ const frames = data.animations[animationName]?.attachments?.[skinName]?.[slotName]?.[attachmentName]?.sequence
115
+ ?? data.animations[animationName]?.attachments?.default?.[slotName]?.[attachmentName]?.sequence;
116
+ return Array.isArray(frames) ? frames as SpineSequenceFrame[] : undefined;
117
+ }
118
+
119
+ function sampleSequenceIndex(
120
+ frames: SpineSequenceFrame[] | undefined,
121
+ time: number,
122
+ sequence: NonNullable<RegionAttachment['sequence']>,
123
+ samplerState: SpineTimelineSamplerState,
124
+ ): number {
125
+ const count = Math.max(1, sequence.count ?? 1);
126
+ if (!Array.isArray(frames) || frames.length === 0) {
127
+ return clampSequenceIndex(sequence.setup ?? 0, count);
128
+ }
129
+ const frameIndex = findCompiledFrameIndex(frames, time, samplerState);
130
+ if (frameIndex < 0) return clampSequenceIndex(sequence.setup ?? 0, count);
131
+ const frame = frames[frameIndex];
132
+ if (!frame) return clampSequenceIndex(sequence.setup ?? 0, count);
133
+ const before = frame.time ?? 0;
134
+ const delay = frame.delay ?? 0;
135
+ let index = frame.index ?? 0;
136
+ const mode = frame.mode ?? 'hold';
137
+ if (mode !== 'hold' && delay > 0) {
138
+ index += Math.floor((time - before) / delay + 0.00001);
139
+ switch (mode) {
140
+ case 'once':
141
+ index = Math.min(count - 1, index);
142
+ break;
143
+ case 'loop':
144
+ index %= count;
145
+ break;
146
+ case 'pingpong': {
147
+ const range = count * 2 - 2;
148
+ index = range === 0 ? 0 : index % range;
149
+ if (index >= count) index = range - index;
150
+ break;
151
+ }
152
+ case 'onceReverse':
153
+ index = Math.max(count - 1 - index, 0);
154
+ break;
155
+ case 'loopReverse':
156
+ index = count - 1 - (index % count);
157
+ break;
158
+ case 'pingpongReverse': {
159
+ const range = count * 2 - 2;
160
+ index = range === 0 ? 0 : (index + count - 1) % range;
161
+ if (index >= count) index = range - index;
162
+ break;
163
+ }
164
+ default:
165
+ break;
166
+ }
167
+ }
168
+ return clampSequenceIndex(index, count);
169
+ }
170
+
171
+ function clampSequenceIndex(index: number, count: number): number {
172
+ return Math.max(0, Math.min(count - 1, Math.floor(index)));
173
+ }
174
+
175
+ function mixColorInto(
176
+ out: [number, number, number, number],
177
+ to: [number, number, number, number],
178
+ mix: number,
179
+ ): void {
180
+ const alpha = Math.max(0, Math.min(1, mix));
181
+ out[0] += (to[0] - out[0]) * alpha;
182
+ out[1] += (to[1] - out[1]) * alpha;
183
+ out[2] += (to[2] - out[2]) * alpha;
184
+ out[3] += (to[3] - out[3]) * alpha;
185
+ }
186
+
187
+ function copyColor(
188
+ source: [number, number, number, number],
189
+ out: [number, number, number, number],
190
+ ): void {
191
+ out[0] = source[0];
192
+ out[1] = source[1];
193
+ out[2] = source[2];
194
+ out[3] = source[3];
195
+ }