@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.
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/animation/Animation2DComponent.d.ts +46 -0
- package/dist/animation/Animation2DExtensionRegistry.d.ts +27 -0
- package/dist/animation/Animation2DRenderSystem.d.ts +89 -0
- package/dist/animation/Animation2DRuntime.d.ts +99 -0
- package/dist/animation/Animation2DStateMachine.d.ts +13 -0
- package/dist/animation/Animation2DStateMachineComponent.d.ts +54 -0
- package/dist/animation/Animation2DStateMachineRuntime.d.ts +30 -0
- package/dist/animation/Animation2DStateMachineSystem.d.ts +14 -0
- package/dist/animation/Animation2DStateMachineVisualRuntime.d.ts +27 -0
- package/dist/animation/Animation2DSystem.d.ts +14 -0
- package/dist/animation/AnimationAssetLoader.d.ts +9 -0
- package/dist/animation/AnimationAudioClip.d.ts +26 -0
- package/dist/animation/AnimationPathTessellator.d.ts +52 -0
- package/dist/animation/AnimationTextRasterizer.d.ts +21 -0
- package/dist/animation/AnimationVisual2D.d.ts +68 -0
- package/dist/animation/HyaAnimation2DClipAdapter.d.ts +16 -0
- package/dist/animation/Particle2DDescriptorAdapter.d.ts +5 -0
- package/dist/animation/runtime/mixer/Action.d.ts +150 -0
- package/dist/animation/runtime/mixer/Mixer.d.ts +54 -0
- package/dist/animation/runtime/mixer/PoseBuffer.d.ts +23 -0
- package/dist/animation/runtime/mixer/Sampler.d.ts +17 -0
- package/dist/animation/runtime/mixer/Types.d.ts +101 -0
- package/dist/animation/runtime/mixer/index.d.ts +6 -0
- package/dist/animation-state-machine/AnimationStateMachine.d.ts +111 -0
- package/dist/animation-state-machine/AnimationStateMachineChannels.d.ts +23 -0
- package/dist/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.d.ts +51 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.d.ts +14 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineCompiler.d.ts +109 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineController.d.ts +103 -0
- package/dist/animation-state-machine/runtime/AnimationStateMachineMixerPort.d.ts +35 -0
- package/dist/animation-state-machine/runtime/index.d.ts +7 -0
- package/dist/animation.d.ts +15 -0
- package/dist/animation.js +1171 -0
- package/dist/animation3d/Animation3DAction.d.ts +46 -0
- package/dist/animation3d/Animation3DBinding.d.ts +85 -0
- package/dist/animation3d/Animation3DClip.d.ts +20 -0
- package/dist/animation3d/Animation3DError.d.ts +13 -0
- package/dist/animation3d/Animation3DMixer.d.ts +27 -0
- package/dist/animation3d/Animation3DPose.d.ts +40 -0
- package/dist/animation3d/Animation3DPoseBuffer.d.ts +24 -0
- package/dist/animation3d/Animation3DResource.d.ts +28 -0
- package/dist/animation3d/Animation3DStateMachine.d.ts +95 -0
- package/dist/animation3d/Animation3DStateMachineRuntime.d.ts +70 -0
- package/dist/animation3d/Animation3DTrack.d.ts +19 -0
- package/dist/animation3d/hya/HyaAnimation3DRuntime.d.ts +73 -0
- package/dist/animation3d/hya/HyaAnimation3DStateMachineRuntime.d.ts +65 -0
- package/dist/animation3d/hya/HyaAnimation3DTypes.d.ts +170 -0
- package/dist/animation3d/hya/index.d.ts +4 -0
- package/dist/animation3d/index.d.ts +14 -0
- package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.d.ts +81 -0
- package/dist/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.d.ts +39 -0
- package/dist/animation3d/runtime/integration/index.d.ts +2 -0
- package/dist/animation3d/runtime/mixer/Action.d.ts +150 -0
- package/dist/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.d.ts +6 -0
- package/dist/animation3d/runtime/mixer/Mixer.d.ts +59 -0
- package/dist/animation3d/runtime/mixer/PoseBuffer.d.ts +1 -0
- package/dist/animation3d/runtime/mixer/TrackSampler.d.ts +24 -0
- package/dist/animation3d/runtime/mixer/index.d.ts +4 -0
- package/dist/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineController.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.d.ts +1 -0
- package/dist/animation3d/runtime/state-machine/index.d.ts +6 -0
- package/dist/animation3d.d.ts +5 -0
- package/dist/animation3d.js +1708 -0
- package/dist/benchmark.d.ts +30 -0
- package/dist/benchmark.js +119 -0
- package/dist/canvas-text/CanvasText2DRenderSystem.d.ts +24 -0
- package/dist/canvas-text/CanvasTextComponent.d.ts +18 -0
- package/dist/canvas-text.d.ts +4 -0
- package/dist/canvas-text.js +276 -0
- package/dist/chunks/2d-ui-animation-2d.generated-D4aHzFU2.js +4 -0
- package/dist/chunks/2d-ui-canvas-text-2d.generated-C0Eaej2m.js +4 -0
- package/dist/chunks/2d-ui-spine2d.generated-CUUi8xjl.js +4 -0
- package/dist/chunks/2d-ui-tilemap2d.generated-gebw4mX7.js +4 -0
- package/dist/chunks/Animation2DStateMachine-9IonguNa.js +3245 -0
- package/dist/chunks/AnimationStateMachineController-BIcAh-sT.js +1438 -0
- package/dist/chunks/GltfAnimation3DAdapter-Cu3rQQsW.js +2240 -0
- package/dist/chunks/GltfAnimationRuntime-CRHsLycN.js +631 -0
- package/dist/chunks/RenderSystem2DBase-DDT61dAu.js +227 -0
- package/dist/chunks/Spine2DRuntime-D9M9LAQR.js +3056 -0
- package/dist/chunks/SpineAssetParser-BSDmPge1.js +378 -0
- package/dist/chunks/gltfLoader-Dm22jLYB.js +2085 -0
- package/dist/chunks/pluginUtils-CvwroRMd.js +33 -0
- package/dist/experimental-gltf-worker.d.ts +4 -0
- package/dist/experimental-gltf-worker.js +138 -0
- package/dist/experimental-spine-worker.d.ts +4 -0
- package/dist/experimental-spine-worker.js +110 -0
- package/dist/gltf/GltfAccessorReader.d.ts +7 -0
- package/dist/gltf/GltfAnimation3DAdapter.d.ts +47 -0
- package/dist/gltf/GltfAnimationRuntime.d.ts +10 -0
- package/dist/gltf/GltfAssetWorkerClient.d.ts +20 -0
- package/dist/gltf/GltfCompatibilityReport.d.ts +70 -0
- package/dist/gltf/GltfConservativeBounds.d.ts +18 -0
- package/dist/gltf/GltfDracoDecoder.d.ts +4 -0
- package/dist/gltf/GltfExtensionAdapter.d.ts +54 -0
- package/dist/gltf/GltfLoaderContract.d.ts +249 -0
- package/dist/gltf/GltfLoaderErrors.d.ts +4 -0
- package/dist/gltf/GltfMaterialDescriptor.d.ts +23 -0
- package/dist/gltf/GltfMaterialEncoder.d.ts +20 -0
- package/dist/gltf/GltfMaterialLoader.d.ts +6 -0
- package/dist/gltf/GltfModelComponent.d.ts +72 -0
- package/dist/gltf/GltfModelSystem.d.ts +37 -0
- package/dist/gltf/GltfPlugin.d.ts +6 -0
- package/dist/gltf/GltfSchema.d.ts +225 -0
- package/dist/gltf/GltfUvSemanticPlanner.d.ts +33 -0
- package/dist/gltf/gltfLoader.d.ts +9 -0
- package/dist/gltf-animation3d.d.ts +3 -0
- package/dist/gltf-animation3d.js +8 -0
- package/dist/gltf-worker-runtime.d.ts +7 -0
- package/dist/gltf-worker-runtime.js +6981 -0
- package/dist/gltf.d.ts +12 -0
- package/dist/gltf.js +517 -0
- package/dist/grid/Grid2DComponent.d.ts +25 -0
- package/dist/grid.d.ts +2 -0
- package/dist/grid.js +56 -0
- package/dist/hya-state-machine.d.ts +7 -0
- package/dist/hya-state-machine.js +2934 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/internal/2d-ui-shader-artifact.js +870 -0
- package/dist/plugins/pluginUtils.d.ts +20 -0
- package/dist/plugins.d.ts +6 -0
- package/dist/shaders/generated/2d-ui-artifact.generated.d.ts +658 -0
- package/dist/spine/Spine2DComponent.d.ts +116 -0
- package/dist/spine/Spine2DGpuRenderer.d.ts +65 -0
- package/dist/spine/Spine2DRenderSystem.d.ts +27 -0
- package/dist/spine/Spine2DRuntime.d.ts +103 -0
- package/dist/spine/SpineAnimationBoneTimelines.d.ts +8 -0
- package/dist/spine/SpineAssetParser.d.ts +18 -0
- package/dist/spine/SpineAssetWorkerClient.d.ts +19 -0
- package/dist/spine/SpineAssetWorkerContract.d.ts +6 -0
- package/dist/spine/SpineAtlasParser.d.ts +21 -0
- package/dist/spine/SpineFloatBuilder.d.ts +15 -0
- package/dist/spine/SpinePathConstraintSolver.d.ts +32 -0
- package/dist/spine/SpinePlugin.d.ts +8 -0
- package/dist/spine/SpineSkeletonRuntime.d.ts +185 -0
- package/dist/spine/SpineSlotAnimation.d.ts +17 -0
- package/dist/spine/SpineTimelineRuntime.d.ts +25 -0
- package/dist/spine/SpineVertexBuilder.d.ts +61 -0
- package/dist/spine.d.ts +7 -0
- package/dist/spine.js +578 -0
- package/dist/tilemap/Tilemap2DComponent.d.ts +110 -0
- package/dist/tilemap/Tilemap2DRenderSystem.d.ts +11 -0
- package/dist/tilemap/Tilemap2DRenderer.d.ts +24 -0
- package/dist/tilemap/TilemapPlugin.d.ts +8 -0
- package/dist/tilemap.d.ts +6 -0
- package/dist/tilemap.js +535 -0
- package/dist/tween/Tween2DComponent.d.ts +70 -0
- package/dist/tween/Tween2DSystem.d.ts +12 -0
- package/dist/tween.d.ts +4 -0
- package/dist/tween.js +138 -0
- package/dist/utils/RenderSystem2DBase.d.ts +42 -0
- package/dist/utils/arrayAccess.d.ts +4 -0
- package/dist/utils/camera2d.d.ts +12 -0
- package/dist/utils/render2dGpu.d.ts +32 -0
- package/package.json +124 -0
- package/src/animation/Animation2DComponent.ts +135 -0
- package/src/animation/Animation2DExtensionRegistry.ts +52 -0
- package/src/animation/Animation2DRenderSystem.ts +1026 -0
- package/src/animation/Animation2DRuntime.ts +1326 -0
- package/src/animation/Animation2DStateMachine.ts +39 -0
- package/src/animation/Animation2DStateMachineComponent.ts +228 -0
- package/src/animation/Animation2DStateMachineRuntime.ts +141 -0
- package/src/animation/Animation2DStateMachineSystem.ts +60 -0
- package/src/animation/Animation2DStateMachineVisualRuntime.ts +282 -0
- package/src/animation/Animation2DSystem.ts +72 -0
- package/src/animation/AnimationAssetLoader.ts +84 -0
- package/src/animation/AnimationAudioClip.ts +140 -0
- package/src/animation/AnimationPathTessellator.ts +691 -0
- package/src/animation/AnimationTextRasterizer.ts +340 -0
- package/src/animation/AnimationVisual2D.ts +153 -0
- package/src/animation/HyaAnimation2DClipAdapter.ts +845 -0
- package/src/animation/Particle2DDescriptorAdapter.ts +35 -0
- package/src/animation/runtime/mixer/Action.ts +876 -0
- package/src/animation/runtime/mixer/Mixer.ts +505 -0
- package/src/animation/runtime/mixer/PoseBuffer.ts +105 -0
- package/src/animation/runtime/mixer/Sampler.ts +252 -0
- package/src/animation/runtime/mixer/Types.ts +138 -0
- package/src/animation/runtime/mixer/index.ts +31 -0
- package/src/animation-state-machine/AnimationStateMachine.ts +146 -0
- package/src/animation-state-machine/AnimationStateMachineChannels.ts +116 -0
- package/src/animation-state-machine/runtime/Animation2DStateMachineMixerAdapter.ts +197 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineBlendTreeWeights.ts +503 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineCompiler.ts +763 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineController.ts +987 -0
- package/src/animation-state-machine/runtime/AnimationStateMachineMixerPort.ts +54 -0
- package/src/animation-state-machine/runtime/index.ts +47 -0
- package/src/animation.ts +33 -0
- package/src/animation3d/Animation3DAction.ts +73 -0
- package/src/animation3d/Animation3DBinding.ts +116 -0
- package/src/animation3d/Animation3DClip.ts +22 -0
- package/src/animation3d/Animation3DError.ts +34 -0
- package/src/animation3d/Animation3DMixer.ts +82 -0
- package/src/animation3d/Animation3DPose.ts +100 -0
- package/src/animation3d/Animation3DPoseBuffer.ts +85 -0
- package/src/animation3d/Animation3DResource.ts +49 -0
- package/src/animation3d/Animation3DStateMachine.ts +126 -0
- package/src/animation3d/Animation3DStateMachineRuntime.ts +191 -0
- package/src/animation3d/Animation3DTrack.ts +29 -0
- package/src/animation3d/hya/HyaAnimation3DRuntime.ts +756 -0
- package/src/animation3d/hya/HyaAnimation3DStateMachineRuntime.ts +618 -0
- package/src/animation3d/hya/HyaAnimation3DTypes.ts +168 -0
- package/src/animation3d/hya/index.ts +10 -0
- package/src/animation3d/index.ts +76 -0
- package/src/animation3d/runtime/integration/Animation3DStateMachineMixerAdapter.ts +325 -0
- package/src/animation3d/runtime/integration/Animation3DStateMachineMixerIntegration.ts +158 -0
- package/src/animation3d/runtime/integration/index.ts +9 -0
- package/src/animation3d/runtime/mixer/Action.ts +833 -0
- package/src/animation3d/runtime/mixer/Animation3DMixerRuntimeStore.ts +26 -0
- package/src/animation3d/runtime/mixer/Mixer.ts +562 -0
- package/src/animation3d/runtime/mixer/PoseBuffer.ts +1 -0
- package/src/animation3d/runtime/mixer/TrackSampler.ts +399 -0
- package/src/animation3d/runtime/mixer/index.ts +8 -0
- package/src/animation3d/runtime/state-machine/Animation3DBlendTreeWeights.ts +4 -0
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineCompiler.ts +1 -0
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineController.ts +1 -0
- package/src/animation3d/runtime/state-machine/Animation3DStateMachineMixerPort.ts +1 -0
- package/src/animation3d/runtime/state-machine/index.ts +45 -0
- package/src/animation3d.ts +5 -0
- package/src/benchmark.ts +168 -0
- package/src/canvas-text/CanvasText2DRenderSystem.ts +292 -0
- package/src/canvas-text/CanvasTextComponent.ts +47 -0
- package/src/canvas-text.ts +4 -0
- package/src/experimental-gltf-worker.ts +18 -0
- package/src/experimental-spine-worker.ts +9 -0
- package/src/gltf/GltfAccessorReader.ts +271 -0
- package/src/gltf/GltfAnimation3DAdapter.ts +680 -0
- package/src/gltf/GltfAnimationRuntime.ts +392 -0
- package/src/gltf/GltfAssetWorkerClient.ts +136 -0
- package/src/gltf/GltfCompatibilityReport.ts +214 -0
- package/src/gltf/GltfConservativeBounds.ts +219 -0
- package/src/gltf/GltfDracoDecoder.ts +251 -0
- package/src/gltf/GltfExtensionAdapter.ts +340 -0
- package/src/gltf/GltfLoaderContract.ts +333 -0
- package/src/gltf/GltfLoaderErrors.ts +59 -0
- package/src/gltf/GltfMaterialDescriptor.ts +134 -0
- package/src/gltf/GltfMaterialEncoder.ts +173 -0
- package/src/gltf/GltfMaterialLoader.ts +293 -0
- package/src/gltf/GltfModelComponent.ts +115 -0
- package/src/gltf/GltfModelSystem.ts +208 -0
- package/src/gltf/GltfPlugin.ts +84 -0
- package/src/gltf/GltfSchema.ts +229 -0
- package/src/gltf/GltfUvSemanticPlanner.ts +166 -0
- package/src/gltf/gltfLoader.ts +910 -0
- package/src/gltf-animation3d.ts +10 -0
- package/src/gltf-worker-runtime.ts +10 -0
- package/src/gltf.ts +56 -0
- package/src/grid/Grid2DComponent.ts +69 -0
- package/src/grid.ts +2 -0
- package/src/hya-state-machine.ts +19 -0
- package/src/index.ts +2 -0
- package/src/plugins/pluginUtils.ts +50 -0
- package/src/plugins.ts +6 -0
- package/src/shaders/generated/2d-ui-animation-2d.generated.wgsl +242 -0
- package/src/shaders/generated/2d-ui-artifact.generated.ts +867 -0
- package/src/shaders/generated/2d-ui-canvas-text-2d.generated.wgsl +38 -0
- package/src/shaders/generated/2d-ui-spine2d.generated.wgsl +41 -0
- package/src/shaders/generated/2d-ui-tilemap2d.generated.wgsl +36 -0
- package/src/spine/Spine2DComponent.ts +168 -0
- package/src/spine/Spine2DGpuRenderer.ts +449 -0
- package/src/spine/Spine2DRenderSystem.ts +199 -0
- package/src/spine/Spine2DRuntime.ts +1529 -0
- package/src/spine/SpineAnimationBoneTimelines.ts +30 -0
- package/src/spine/SpineAssetParser.ts +20 -0
- package/src/spine/SpineAssetWorkerClient.ts +112 -0
- package/src/spine/SpineAssetWorkerContract.ts +4 -0
- package/src/spine/SpineAtlasParser.ts +127 -0
- package/src/spine/SpineFloatBuilder.ts +66 -0
- package/src/spine/SpinePathConstraintSolver.ts +582 -0
- package/src/spine/SpinePlugin.ts +76 -0
- package/src/spine/SpineSkeletonRuntime.ts +521 -0
- package/src/spine/SpineSlotAnimation.ts +195 -0
- package/src/spine/SpineTimelineRuntime.ts +453 -0
- package/src/spine/SpineVertexBuilder.ts +686 -0
- package/src/spine.ts +7 -0
- package/src/tilemap/Tilemap2DComponent.ts +232 -0
- package/src/tilemap/Tilemap2DRenderSystem.ts +58 -0
- package/src/tilemap/Tilemap2DRenderer.ts +289 -0
- package/src/tilemap/TilemapPlugin.ts +75 -0
- package/src/tilemap.ts +6 -0
- package/src/tween/Tween2DComponent.ts +83 -0
- package/src/tween/Tween2DSystem.ts +78 -0
- package/src/tween.ts +4 -0
- package/src/types/earcut.d.ts +7 -0
- package/src/types/wgsl.d.ts +4 -0
- package/src/utils/RenderSystem2DBase.ts +164 -0
- package/src/utils/arrayAccess.ts +17 -0
- package/src/utils/camera2d.ts +68 -0
- package/src/utils/render2dGpu.ts +132 -0
- package/test/fixtures/gltf/animation-characterization.gltf +344 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
Animation2DBinding,
|
|
3
|
+
Animation2DDiscreteTrack,
|
|
4
|
+
Animation2DNumericTrack,
|
|
5
|
+
Animation2DTrack,
|
|
6
|
+
} from './Types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Stateful allocation-free sampler. Rotation values are radians and LINEAR
|
|
10
|
+
* sampling follows the shortest signed arc in [-PI, PI).
|
|
11
|
+
*/
|
|
12
|
+
export class Animation2DSampler {
|
|
13
|
+
readonly track: Animation2DTrack;
|
|
14
|
+
readonly output: Float32Array;
|
|
15
|
+
discreteValue: unknown;
|
|
16
|
+
|
|
17
|
+
private _cursor = 0;
|
|
18
|
+
|
|
19
|
+
constructor(track: Animation2DTrack) {
|
|
20
|
+
validateTrack(track);
|
|
21
|
+
this.track = track;
|
|
22
|
+
this.output = new Float32Array(isNumericBinding(track.binding)
|
|
23
|
+
? track.binding.valueSize!
|
|
24
|
+
: 0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
sample(timeSeconds: number, out: Float32Array = this.output): Float32Array | unknown {
|
|
28
|
+
if (!Number.isFinite(timeSeconds)) {
|
|
29
|
+
throw new RangeError(`Animation2D sample time must be finite; received ${timeSeconds}.`);
|
|
30
|
+
}
|
|
31
|
+
const keyIndex = this._findKey(timeSeconds);
|
|
32
|
+
if (!isNumericBinding(this.track.binding)) {
|
|
33
|
+
this.discreteValue = (this.track as Animation2DDiscreteTrack).values[keyIndex];
|
|
34
|
+
return this.discreteValue;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const track = this.track as Animation2DNumericTrack;
|
|
38
|
+
const size = track.binding.valueSize!;
|
|
39
|
+
if (out.length < size) {
|
|
40
|
+
throw new RangeError(`Animation2D sampler output requires ${size} values; received ${out.length}.`);
|
|
41
|
+
}
|
|
42
|
+
const times = track.times;
|
|
43
|
+
if (keyIndex >= times.length - 1 || track.interpolation === 'step') {
|
|
44
|
+
copyNumericKey(track, keyIndex, out);
|
|
45
|
+
return out;
|
|
46
|
+
}
|
|
47
|
+
const startTime = times[keyIndex]!;
|
|
48
|
+
const linearAlpha = (timeSeconds - startTime) / (times[keyIndex + 1]! - startTime);
|
|
49
|
+
const alpha = track.interpolation === 'cubic-bezier'
|
|
50
|
+
? sampleCubicEasing(track.easings!, keyIndex, linearAlpha)
|
|
51
|
+
: linearAlpha;
|
|
52
|
+
const startOffset = keyIndex * size;
|
|
53
|
+
const endOffset = startOffset + size;
|
|
54
|
+
if (track.binding.strategy === 'rotation') {
|
|
55
|
+
const start = track.values[startOffset]!;
|
|
56
|
+
const delta = wrapAngle(track.values[endOffset]! - start);
|
|
57
|
+
out[0] = wrapAngle(start + delta * alpha);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
if (track.spatialTangents) {
|
|
61
|
+
const tangentOffset = keyIndex * 4;
|
|
62
|
+
for (let component = 0; component < 2; component++) {
|
|
63
|
+
const start = track.values[startOffset + component]!;
|
|
64
|
+
const end = track.values[endOffset + component]!;
|
|
65
|
+
out[component] = cubicSpatialCoordinate(
|
|
66
|
+
alpha,
|
|
67
|
+
start,
|
|
68
|
+
start + track.spatialTangents[tangentOffset + component]!,
|
|
69
|
+
end + track.spatialTangents[tangentOffset + 2 + component]!,
|
|
70
|
+
end,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
for (let component = 0; component < size; component++) {
|
|
76
|
+
const start = track.values[startOffset + component]!;
|
|
77
|
+
out[component] = start
|
|
78
|
+
+ (track.values[endOffset + component]! - start) * alpha;
|
|
79
|
+
}
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
resetCursor(): void {
|
|
84
|
+
this._cursor = 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private _findKey(timeSeconds: number): number {
|
|
88
|
+
const times = this.track.times;
|
|
89
|
+
const last = times.length - 1;
|
|
90
|
+
if (last === 0 || timeSeconds <= times[0]!) {
|
|
91
|
+
this._cursor = 0;
|
|
92
|
+
return 0;
|
|
93
|
+
}
|
|
94
|
+
if (timeSeconds >= times[last]!) {
|
|
95
|
+
this._cursor = Math.max(0, last - 1);
|
|
96
|
+
return last;
|
|
97
|
+
}
|
|
98
|
+
let cursor = Math.min(this._cursor, last - 1);
|
|
99
|
+
if (timeSeconds >= times[cursor]! && timeSeconds < times[cursor + 1]!) return cursor;
|
|
100
|
+
if (timeSeconds >= times[cursor + 1]!) {
|
|
101
|
+
while (cursor + 1 < last && timeSeconds >= times[cursor + 1]!) cursor++;
|
|
102
|
+
this._cursor = cursor;
|
|
103
|
+
return cursor;
|
|
104
|
+
}
|
|
105
|
+
let low = 0;
|
|
106
|
+
let high = cursor;
|
|
107
|
+
while (low <= high) {
|
|
108
|
+
const middle = (low + high) >>> 1;
|
|
109
|
+
if (timeSeconds < times[middle]!) {
|
|
110
|
+
high = middle - 1;
|
|
111
|
+
} else if (timeSeconds >= times[middle + 1]!) {
|
|
112
|
+
low = middle + 1;
|
|
113
|
+
} else {
|
|
114
|
+
this._cursor = middle;
|
|
115
|
+
return middle;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
this._cursor = Math.max(0, low);
|
|
119
|
+
return this._cursor;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function wrapAngle(value: number): number {
|
|
124
|
+
const twoPi = Math.PI * 2;
|
|
125
|
+
let wrapped = (value + Math.PI) % twoPi;
|
|
126
|
+
if (wrapped < 0) wrapped += twoPi;
|
|
127
|
+
return wrapped - Math.PI;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function isNumericBinding(binding: Animation2DBinding): boolean {
|
|
131
|
+
return binding.strategy === 'continuous' || binding.strategy === 'rotation';
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function copyNumericKey(
|
|
135
|
+
track: Animation2DNumericTrack,
|
|
136
|
+
keyIndex: number,
|
|
137
|
+
out: Float32Array,
|
|
138
|
+
): void {
|
|
139
|
+
const size = track.binding.valueSize!;
|
|
140
|
+
const offset = keyIndex * size;
|
|
141
|
+
for (let component = 0; component < size; component++) {
|
|
142
|
+
out[component] = track.values[offset + component]!;
|
|
143
|
+
}
|
|
144
|
+
if (track.binding.strategy === 'rotation') out[0] = wrapAngle(out[0]!);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function validateTrack(track: Animation2DTrack): void {
|
|
148
|
+
if (!track.id) throw new RangeError('Animation2D track id must not be empty.');
|
|
149
|
+
if (!track.binding.id) throw new RangeError('Animation2D binding id must not be empty.');
|
|
150
|
+
if (track.times.length === 0) {
|
|
151
|
+
throw new RangeError(`Animation2D track "${track.id}" must contain at least one key.`);
|
|
152
|
+
}
|
|
153
|
+
for (let index = 0; index < track.times.length; index++) {
|
|
154
|
+
const time = track.times[index]!;
|
|
155
|
+
if (!Number.isFinite(time) || (index > 0 && time <= track.times[index - 1]!)) {
|
|
156
|
+
throw new RangeError(`Animation2D track "${track.id}" times must be finite and strictly increasing.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (isNumericBinding(track.binding)) {
|
|
160
|
+
const numericTrack = track as Animation2DNumericTrack;
|
|
161
|
+
const size = track.binding.valueSize ?? 0;
|
|
162
|
+
if (!Number.isInteger(size) || size <= 0) {
|
|
163
|
+
throw new RangeError(`Animation2D numeric binding "${track.binding.id}" requires a positive valueSize.`);
|
|
164
|
+
}
|
|
165
|
+
if (track.binding.strategy === 'rotation' && size !== 1) {
|
|
166
|
+
throw new RangeError(`Animation2D rotation binding "${track.binding.id}" must have valueSize 1.`);
|
|
167
|
+
}
|
|
168
|
+
const expected = track.times.length * size;
|
|
169
|
+
if (!(track.values instanceof Float32Array) || track.values.length !== expected) {
|
|
170
|
+
throw new RangeError(`Animation2D track "${track.id}" requires ${expected} numeric values.`);
|
|
171
|
+
}
|
|
172
|
+
if (numericTrack.interpolation === 'cubic-bezier') {
|
|
173
|
+
const expectedEasings = Math.max(0, track.times.length - 1) * 4;
|
|
174
|
+
if (!(numericTrack.easings instanceof Float32Array) || numericTrack.easings.length !== expectedEasings) {
|
|
175
|
+
throw new RangeError(`Animation2D track "${track.id}" requires ${expectedEasings} cubic easing values.`);
|
|
176
|
+
}
|
|
177
|
+
for (let index = 0; index < numericTrack.easings.length; index += 4) {
|
|
178
|
+
const x1 = numericTrack.easings[index]!;
|
|
179
|
+
const x2 = numericTrack.easings[index + 2]!;
|
|
180
|
+
if (x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) {
|
|
181
|
+
throw new RangeError(`Animation2D track "${track.id}" cubic easing x controls must be in [0, 1].`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else if (numericTrack.easings !== undefined) {
|
|
185
|
+
throw new RangeError(`Animation2D track "${track.id}" easings require cubic-bezier interpolation.`);
|
|
186
|
+
}
|
|
187
|
+
if (numericTrack.spatialTangents !== undefined) {
|
|
188
|
+
const expectedTangents = Math.max(0, track.times.length - 1) * 4;
|
|
189
|
+
if (track.binding.path !== 'transform.position' || size !== 2) {
|
|
190
|
+
throw new RangeError(`Animation2D track "${track.id}" spatialTangents require transform.position/2.`);
|
|
191
|
+
}
|
|
192
|
+
if (numericTrack.interpolation === 'step') {
|
|
193
|
+
throw new RangeError(`Animation2D track "${track.id}" spatialTangents cannot use step.`);
|
|
194
|
+
}
|
|
195
|
+
if (!(numericTrack.spatialTangents instanceof Float32Array)
|
|
196
|
+
|| numericTrack.spatialTangents.length !== expectedTangents) {
|
|
197
|
+
throw new RangeError(`Animation2D track "${track.id}" requires ${expectedTangents} spatialTangents.`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
if (track.interpolation !== 'step') {
|
|
203
|
+
throw new RangeError(`Animation2D ${track.binding.strategy} track "${track.id}" must use step interpolation.`);
|
|
204
|
+
}
|
|
205
|
+
if (track.values.length !== track.times.length) {
|
|
206
|
+
throw new RangeError(`Animation2D track "${track.id}" requires one discrete value per key.`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function sampleCubicEasing(easings: Float32Array, segment: number, x: number): number {
|
|
211
|
+
const offset = segment * 4;
|
|
212
|
+
const x1 = easings[offset]!;
|
|
213
|
+
const y1 = easings[offset + 1]!;
|
|
214
|
+
const x2 = easings[offset + 2]!;
|
|
215
|
+
const y2 = easings[offset + 3]!;
|
|
216
|
+
let parameter = x;
|
|
217
|
+
for (let iteration = 0; iteration < 6; iteration++) {
|
|
218
|
+
const estimate = cubicCoordinate(parameter, x1, x2) - x;
|
|
219
|
+
const derivative = cubicDerivative(parameter, x1, x2);
|
|
220
|
+
if (Math.abs(derivative) < 1e-6) break;
|
|
221
|
+
parameter = Math.min(1, Math.max(0, parameter - estimate / derivative));
|
|
222
|
+
}
|
|
223
|
+
return cubicCoordinate(parameter, y1, y2);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function cubicCoordinate(value: number, first: number, second: number): number {
|
|
227
|
+
const inverse = 1 - value;
|
|
228
|
+
return 3 * inverse * inverse * value * first
|
|
229
|
+
+ 3 * inverse * value * value * second
|
|
230
|
+
+ value * value * value;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function cubicDerivative(value: number, first: number, second: number): number {
|
|
234
|
+
const inverse = 1 - value;
|
|
235
|
+
return 3 * inverse * inverse * first
|
|
236
|
+
+ 6 * inverse * value * (second - first)
|
|
237
|
+
+ 3 * value * value * (1 - second);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function cubicSpatialCoordinate(
|
|
241
|
+
value: number,
|
|
242
|
+
start: number,
|
|
243
|
+
first: number,
|
|
244
|
+
second: number,
|
|
245
|
+
end: number,
|
|
246
|
+
): number {
|
|
247
|
+
const inverse = 1 - value;
|
|
248
|
+
return inverse * inverse * inverse * start
|
|
249
|
+
+ 3 * inverse * inverse * value * first
|
|
250
|
+
+ 3 * inverse * value * value * second
|
|
251
|
+
+ value * value * value * end;
|
|
252
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnimationStateMachineBindingMask,
|
|
3
|
+
AnimationStateMachineBlendMode,
|
|
4
|
+
AnimationStateMachineLoopMode,
|
|
5
|
+
} from '../../../animation-state-machine/AnimationStateMachine.js';
|
|
6
|
+
|
|
7
|
+
export type Animation2DBindingStrategy =
|
|
8
|
+
| 'continuous'
|
|
9
|
+
| 'rotation'
|
|
10
|
+
| 'discrete'
|
|
11
|
+
| 'dominant';
|
|
12
|
+
|
|
13
|
+
export interface Animation2DBinding {
|
|
14
|
+
/** Stable mask and pose channel id. */
|
|
15
|
+
readonly id: string;
|
|
16
|
+
/** Stable node, slot, component, or graph target id. */
|
|
17
|
+
readonly targetId: string;
|
|
18
|
+
/** Examples: transform.position, opacity, text, sprite, composite, mask. */
|
|
19
|
+
readonly path: string;
|
|
20
|
+
readonly strategy: Animation2DBindingStrategy;
|
|
21
|
+
/** Required for continuous and rotation bindings; rotation must use one value. */
|
|
22
|
+
readonly valueSize?: number;
|
|
23
|
+
/** Base used when a continuous layer has less than unit weight. */
|
|
24
|
+
readonly defaultValue?: Readonly<ArrayLike<number>> | unknown;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type Animation2DInterpolation = 'step' | 'linear' | 'cubic-bezier';
|
|
28
|
+
|
|
29
|
+
export interface Animation2DNumericTrack {
|
|
30
|
+
readonly id: string;
|
|
31
|
+
readonly binding: Animation2DBinding;
|
|
32
|
+
readonly interpolation: Animation2DInterpolation;
|
|
33
|
+
readonly times: Float32Array;
|
|
34
|
+
/** Tightly packed key values by binding.valueSize. */
|
|
35
|
+
readonly values: Float32Array;
|
|
36
|
+
/** Per-segment x1, y1, x2, y2 tuples for cubic-bezier interpolation. */
|
|
37
|
+
readonly easings?: Float32Array;
|
|
38
|
+
/** Relative outgoing/incoming spatial controls for transform.position. */
|
|
39
|
+
readonly spatialTangents?: Float32Array;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface Animation2DDiscreteTrack {
|
|
43
|
+
readonly id: string;
|
|
44
|
+
readonly binding: Animation2DBinding;
|
|
45
|
+
readonly interpolation: 'step';
|
|
46
|
+
/** One immutable/reference-stable value per key. */
|
|
47
|
+
readonly times: Float32Array;
|
|
48
|
+
readonly values: readonly unknown[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type Animation2DTrack =
|
|
52
|
+
| Animation2DNumericTrack
|
|
53
|
+
| Animation2DDiscreteTrack;
|
|
54
|
+
|
|
55
|
+
export type Animation2DEffectKind = 'audio' | 'particle';
|
|
56
|
+
export type Animation2DEffectLoopBehavior = 'continue' | 'restart';
|
|
57
|
+
|
|
58
|
+
export interface Animation2DEffectCue {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
readonly kind: Animation2DEffectKind;
|
|
61
|
+
readonly start: number;
|
|
62
|
+
/** Defaults to clip duration. The interval is [start, end). */
|
|
63
|
+
readonly end?: number;
|
|
64
|
+
/** Audio defaults to continue; particle defaults to restart. */
|
|
65
|
+
readonly loopBehavior?: Animation2DEffectLoopBehavior;
|
|
66
|
+
readonly payload?: unknown;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface Animation2DClip {
|
|
70
|
+
readonly format: 'haiyue-animation2d-clip@1';
|
|
71
|
+
readonly id: string;
|
|
72
|
+
readonly name: string;
|
|
73
|
+
readonly duration: number;
|
|
74
|
+
readonly tracks: readonly Animation2DTrack[];
|
|
75
|
+
readonly effects?: readonly Animation2DEffectCue[];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type Animation2DEffectLifecycle =
|
|
79
|
+
| 'enter'
|
|
80
|
+
| 'exit'
|
|
81
|
+
| 'restart'
|
|
82
|
+
| 'loop'
|
|
83
|
+
| 'seek';
|
|
84
|
+
|
|
85
|
+
export interface Animation2DEffectEvent {
|
|
86
|
+
readonly actionId: string;
|
|
87
|
+
readonly clipId: string;
|
|
88
|
+
readonly cue: Animation2DEffectCue;
|
|
89
|
+
readonly lifecycle: Animation2DEffectLifecycle;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface Animation2DPoseChannel {
|
|
93
|
+
readonly binding: Animation2DBinding;
|
|
94
|
+
/**
|
|
95
|
+
* Continuous and rotation channels expose a reused Float32Array. Discrete
|
|
96
|
+
* and dominant channels expose the winning authored value by reference.
|
|
97
|
+
*/
|
|
98
|
+
readonly value: Readonly<Float32Array> | unknown;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface Animation2DPose {
|
|
102
|
+
readonly sequence: number;
|
|
103
|
+
readonly mixerTime: number;
|
|
104
|
+
readonly channels: readonly Animation2DPoseChannel[];
|
|
105
|
+
readonly effects: readonly Animation2DEffectEvent[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface Animation2DMutablePose {
|
|
109
|
+
reset(mixerTime: number): void;
|
|
110
|
+
writeNumeric(binding: Animation2DBinding, value: ArrayLike<number>): void;
|
|
111
|
+
writeDiscrete(binding: Animation2DBinding, value: unknown): void;
|
|
112
|
+
emit(effect: Animation2DEffectEvent): void;
|
|
113
|
+
seal(): Animation2DPose;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface Animation2DActionOptions {
|
|
117
|
+
readonly id?: string;
|
|
118
|
+
readonly loop?: AnimationStateMachineLoopMode;
|
|
119
|
+
readonly repetitions?: number;
|
|
120
|
+
readonly clampWhenFinished?: boolean;
|
|
121
|
+
readonly timeScale?: number;
|
|
122
|
+
readonly weight?: number;
|
|
123
|
+
/** Ascending layers are applied in order; a higher layer overrides a lower one. */
|
|
124
|
+
readonly layer?: number;
|
|
125
|
+
readonly blendMode?: AnimationStateMachineBlendMode;
|
|
126
|
+
readonly mask?: AnimationStateMachineBindingMask;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export type Animation2DActionStatus =
|
|
130
|
+
| 'idle'
|
|
131
|
+
| 'running'
|
|
132
|
+
| 'paused'
|
|
133
|
+
| 'finished'
|
|
134
|
+
| 'stopped';
|
|
135
|
+
|
|
136
|
+
export type Animation2DLoopMode = AnimationStateMachineLoopMode;
|
|
137
|
+
export type Animation2DBlendMode = AnimationStateMachineBlendMode;
|
|
138
|
+
export type Animation2DBindingMask = AnimationStateMachineBindingMask;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export { Animation2DActionRuntime } from './Action.js';
|
|
2
|
+
export type {
|
|
3
|
+
Animation2DActionOwner,
|
|
4
|
+
Animation2DActionSynchronizedState,
|
|
5
|
+
Animation2DActionTrackRuntime,
|
|
6
|
+
} from './Action.js';
|
|
7
|
+
export { Animation2DMixerRuntime } from './Mixer.js';
|
|
8
|
+
export { Animation2DPoseBuffer } from './PoseBuffer.js';
|
|
9
|
+
export { Animation2DSampler, wrapAngle } from './Sampler.js';
|
|
10
|
+
export type {
|
|
11
|
+
Animation2DActionOptions,
|
|
12
|
+
Animation2DActionStatus,
|
|
13
|
+
Animation2DBinding,
|
|
14
|
+
Animation2DBindingMask,
|
|
15
|
+
Animation2DBindingStrategy,
|
|
16
|
+
Animation2DBlendMode,
|
|
17
|
+
Animation2DClip,
|
|
18
|
+
Animation2DDiscreteTrack,
|
|
19
|
+
Animation2DEffectCue,
|
|
20
|
+
Animation2DEffectEvent,
|
|
21
|
+
Animation2DEffectKind,
|
|
22
|
+
Animation2DEffectLifecycle,
|
|
23
|
+
Animation2DEffectLoopBehavior,
|
|
24
|
+
Animation2DInterpolation,
|
|
25
|
+
Animation2DLoopMode,
|
|
26
|
+
Animation2DMutablePose,
|
|
27
|
+
Animation2DNumericTrack,
|
|
28
|
+
Animation2DPose,
|
|
29
|
+
Animation2DPoseChannel,
|
|
30
|
+
Animation2DTrack,
|
|
31
|
+
} from './Types.js';
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
export type AnimationStateMachineLoopMode =
|
|
2
|
+
| 'once'
|
|
3
|
+
| 'repeat'
|
|
4
|
+
| 'ping-pong';
|
|
5
|
+
|
|
6
|
+
export type AnimationStateMachineBlendMode =
|
|
7
|
+
| 'override'
|
|
8
|
+
| 'additive';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A state-machine mask addresses logical animation bindings. The concrete 2D
|
|
12
|
+
* or 3D mixer decides how those stable ids resolve to runtime targets.
|
|
13
|
+
*/
|
|
14
|
+
export interface AnimationStateMachineBindingMask {
|
|
15
|
+
/** Empty or omitted include means all bindings are eligible. */
|
|
16
|
+
readonly include?: readonly string[];
|
|
17
|
+
readonly exclude?: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type AnimationStateMachineParameterDefinition =
|
|
21
|
+
| Readonly<{
|
|
22
|
+
name: string;
|
|
23
|
+
type: 'float';
|
|
24
|
+
defaultValue: number;
|
|
25
|
+
}>
|
|
26
|
+
| Readonly<{
|
|
27
|
+
name: string;
|
|
28
|
+
type: 'integer';
|
|
29
|
+
defaultValue: number;
|
|
30
|
+
}>
|
|
31
|
+
| Readonly<{
|
|
32
|
+
name: string;
|
|
33
|
+
type: 'boolean';
|
|
34
|
+
defaultValue: boolean;
|
|
35
|
+
}>
|
|
36
|
+
| Readonly<{
|
|
37
|
+
name: string;
|
|
38
|
+
type: 'trigger';
|
|
39
|
+
}>;
|
|
40
|
+
|
|
41
|
+
export type AnimationStateMachineTransitionCondition =
|
|
42
|
+
| Readonly<{
|
|
43
|
+
parameter: string;
|
|
44
|
+
operator: 'greater' | 'greater-or-equal' | 'less' | 'less-or-equal';
|
|
45
|
+
value: number;
|
|
46
|
+
}>
|
|
47
|
+
| Readonly<{
|
|
48
|
+
parameter: string;
|
|
49
|
+
operator: 'equal' | 'not-equal';
|
|
50
|
+
value: number | boolean;
|
|
51
|
+
}>
|
|
52
|
+
| Readonly<{
|
|
53
|
+
parameter: string;
|
|
54
|
+
operator: 'is-true' | 'is-false' | 'triggered';
|
|
55
|
+
}>;
|
|
56
|
+
|
|
57
|
+
export interface AnimationStateMachineClipMotion {
|
|
58
|
+
readonly kind: 'clip';
|
|
59
|
+
/**
|
|
60
|
+
* Mixer-owned source id. It may identify a 3D clip, a HYA composition, or
|
|
61
|
+
* another animation source understood by the selected mixer port.
|
|
62
|
+
*/
|
|
63
|
+
readonly clipId: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface AnimationStateMachineBlend1DChild {
|
|
67
|
+
readonly threshold: number;
|
|
68
|
+
readonly motion: AnimationStateMachineMotionDefinition;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface AnimationStateMachineBlend1DMotion {
|
|
72
|
+
readonly kind: 'blend-1d';
|
|
73
|
+
readonly parameter: string;
|
|
74
|
+
readonly children: readonly AnimationStateMachineBlend1DChild[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface AnimationStateMachineBlend2DChild {
|
|
78
|
+
readonly position: readonly [number, number];
|
|
79
|
+
readonly motion: AnimationStateMachineMotionDefinition;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface AnimationStateMachineBlend2DMotion {
|
|
83
|
+
readonly kind: 'blend-2d';
|
|
84
|
+
readonly algorithm: 'cartesian' | 'directional';
|
|
85
|
+
readonly parameterX: string;
|
|
86
|
+
readonly parameterY: string;
|
|
87
|
+
readonly children: readonly AnimationStateMachineBlend2DChild[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export type AnimationStateMachineMotionDefinition =
|
|
91
|
+
| AnimationStateMachineClipMotion
|
|
92
|
+
| AnimationStateMachineBlend1DMotion
|
|
93
|
+
| AnimationStateMachineBlend2DMotion;
|
|
94
|
+
|
|
95
|
+
export interface AnimationStateMachineStateDefinition {
|
|
96
|
+
readonly id: string;
|
|
97
|
+
readonly name: string;
|
|
98
|
+
readonly motion: AnimationStateMachineMotionDefinition;
|
|
99
|
+
readonly speed?: number;
|
|
100
|
+
readonly speedParameter?: string;
|
|
101
|
+
readonly loop?: AnimationStateMachineLoopMode;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export type AnimationStateMachineTransitionInterruption =
|
|
105
|
+
| 'none'
|
|
106
|
+
| 'source'
|
|
107
|
+
| 'destination'
|
|
108
|
+
| 'source-then-destination'
|
|
109
|
+
| 'destination-then-source';
|
|
110
|
+
|
|
111
|
+
export interface AnimationStateMachineTransitionDefinition {
|
|
112
|
+
readonly id: string;
|
|
113
|
+
/** '*' means any state in the same layer. */
|
|
114
|
+
readonly from: string | '*';
|
|
115
|
+
readonly to: string;
|
|
116
|
+
readonly conditions: readonly AnimationStateMachineTransitionCondition[];
|
|
117
|
+
readonly duration: number;
|
|
118
|
+
readonly hasExitTime?: boolean;
|
|
119
|
+
readonly exitTime?: number;
|
|
120
|
+
readonly destinationOffset?: number;
|
|
121
|
+
readonly interruption?: AnimationStateMachineTransitionInterruption;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface AnimationStateMachineLayer {
|
|
125
|
+
readonly id: string;
|
|
126
|
+
readonly name: string;
|
|
127
|
+
readonly initialStateId: string;
|
|
128
|
+
readonly states: readonly AnimationStateMachineStateDefinition[];
|
|
129
|
+
/** Declaration order is transition priority. */
|
|
130
|
+
readonly transitions: readonly AnimationStateMachineTransitionDefinition[];
|
|
131
|
+
readonly blendMode?: AnimationStateMachineBlendMode;
|
|
132
|
+
readonly weight?: number;
|
|
133
|
+
readonly mask?: AnimationStateMachineBindingMask;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Dimension-neutral serializable state-machine definition. Runtime controller
|
|
138
|
+
* state and concrete mixer objects are intentionally not stored here.
|
|
139
|
+
*/
|
|
140
|
+
export interface AnimationStateMachineDefinition {
|
|
141
|
+
readonly format: 'haiyue-animation-state-machine@1';
|
|
142
|
+
readonly id: string;
|
|
143
|
+
readonly name: string;
|
|
144
|
+
readonly parameters: readonly AnimationStateMachineParameterDefinition[];
|
|
145
|
+
readonly layers: readonly AnimationStateMachineLayer[];
|
|
146
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HYA_STATE_MACHINE_CHANNEL_REGISTRY,
|
|
3
|
+
hyaStateMachineChannelCapability,
|
|
4
|
+
type HyaStateMachineChannelCapability,
|
|
5
|
+
type HyaStateMachineChannelId,
|
|
6
|
+
type HyaStateMachineDefinition,
|
|
7
|
+
type HyaStateMachineMotion,
|
|
8
|
+
} from '@haiyue/animation-spec';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
HYA_STATE_MACHINE_CHANNEL_REGISTRY,
|
|
12
|
+
hyaStateMachineChannelCapability,
|
|
13
|
+
};
|
|
14
|
+
export type {
|
|
15
|
+
HyaStateMachineChannelCapability,
|
|
16
|
+
HyaStateMachineChannelId,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export type AnimationStateMachineChannelDiagnosticCode =
|
|
20
|
+
| NonNullable<HyaStateMachineChannelCapability['diagnosticCode']>
|
|
21
|
+
| 'W_STATE_MACHINE_CHANNEL_AUDIO_AUTOPLAY_REJECTED';
|
|
22
|
+
|
|
23
|
+
export interface AnimationStateMachineChannelDiagnostic {
|
|
24
|
+
readonly code: AnimationStateMachineChannelDiagnosticCode;
|
|
25
|
+
readonly severity: 'warning' | 'error';
|
|
26
|
+
readonly channelId: HyaStateMachineChannelId;
|
|
27
|
+
readonly path: string;
|
|
28
|
+
readonly message: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class AnimationStateMachineChannelError extends Error {
|
|
32
|
+
readonly name = 'AnimationStateMachineChannelError';
|
|
33
|
+
|
|
34
|
+
constructor(readonly diagnostic: AnimationStateMachineChannelDiagnostic) {
|
|
35
|
+
super(`${diagnostic.message} (${diagnostic.path})`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Audio owns one HTMLMediaElement playhead and is intentionally not amplitude
|
|
41
|
+
* mixed. It is executable only when the graph can never overlap two motions.
|
|
42
|
+
*/
|
|
43
|
+
export function audioStateMachineCompatibilityDiagnostic(
|
|
44
|
+
definition: HyaStateMachineDefinition,
|
|
45
|
+
path = '$.stateMachine',
|
|
46
|
+
): AnimationStateMachineChannelDiagnostic | null {
|
|
47
|
+
const capability = hyaStateMachineChannelCapability('audio');
|
|
48
|
+
if (definition.layers.length !== 1) {
|
|
49
|
+
return audioRangeDiagnostic(`${path}.layers`, 'Audio requires exactly one state-machine layer.');
|
|
50
|
+
}
|
|
51
|
+
const layer = definition.layers[0]!;
|
|
52
|
+
if ((layer.weight ?? 1) !== 1 || (layer.blendMode ?? 'override') !== 'override') {
|
|
53
|
+
return audioRangeDiagnostic(
|
|
54
|
+
`${path}.layers[0]`,
|
|
55
|
+
'Audio requires one full-weight override layer because media amplitude is not pose-mixable.',
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
for (let stateIndex = 0; stateIndex < layer.states.length; stateIndex++) {
|
|
59
|
+
if (motionContainsBlendTree(layer.states[stateIndex]!.motion)) {
|
|
60
|
+
return audioRangeDiagnostic(
|
|
61
|
+
`${path}.layers[0].states[${stateIndex}].motion`,
|
|
62
|
+
'Audio cannot run inside a Blend Tree because two media playheads would overlap.',
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
for (let transitionIndex = 0; transitionIndex < layer.transitions.length; transitionIndex++) {
|
|
67
|
+
if (layer.transitions[transitionIndex]!.duration > 0) {
|
|
68
|
+
return audioRangeDiagnostic(
|
|
69
|
+
`${path}.layers[0].transitions[${transitionIndex}].duration`,
|
|
70
|
+
'Audio transitions must be immediate; non-zero cross-fades are an unmixable media range.',
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (capability.transition !== 'immediate-only') {
|
|
75
|
+
throw new Error('The canonical audio channel contract is internally inconsistent.');
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function assertAudioStateMachineCompatible(
|
|
81
|
+
definition: HyaStateMachineDefinition,
|
|
82
|
+
path?: string,
|
|
83
|
+
): void {
|
|
84
|
+
const diagnostic = audioStateMachineCompatibilityDiagnostic(definition, path);
|
|
85
|
+
if (diagnostic) throw new AnimationStateMachineChannelError(diagnostic);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function audioAutoplayRejectedDiagnostic(
|
|
89
|
+
path: string,
|
|
90
|
+
reason?: unknown,
|
|
91
|
+
): AnimationStateMachineChannelDiagnostic {
|
|
92
|
+
const suffix = reason instanceof Error && reason.message
|
|
93
|
+
? ` Browser reason: ${reason.message}`
|
|
94
|
+
: '';
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
code: 'W_STATE_MACHINE_CHANNEL_AUDIO_AUTOPLAY_REJECTED',
|
|
97
|
+
severity: 'warning',
|
|
98
|
+
channelId: 'audio',
|
|
99
|
+
path,
|
|
100
|
+
message: `Audio play() was rejected; the playhead remains paused until a later user-authorized resume.${suffix}`,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function audioRangeDiagnostic(path: string, message: string): AnimationStateMachineChannelDiagnostic {
|
|
105
|
+
return Object.freeze({
|
|
106
|
+
code: 'E_STATE_MACHINE_CHANNEL_AUDIO_UNMIXABLE_RANGE',
|
|
107
|
+
severity: 'error',
|
|
108
|
+
channelId: 'audio',
|
|
109
|
+
path,
|
|
110
|
+
message,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function motionContainsBlendTree(motion: HyaStateMachineMotion): boolean {
|
|
115
|
+
return motion.kind !== 'clip';
|
|
116
|
+
}
|