@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,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Animation2DActionRuntime,
|
|
3
|
+
type Animation2DActionSynchronizedState,
|
|
4
|
+
} from '../../animation/runtime/mixer/Action.js';
|
|
5
|
+
import { Animation2DMixerRuntime } from '../../animation/runtime/mixer/Mixer.js';
|
|
6
|
+
import type {
|
|
7
|
+
AnimationStateMachineActionOptions,
|
|
8
|
+
AnimationStateMachineMixerPort,
|
|
9
|
+
} from './AnimationStateMachineMixerPort.js';
|
|
10
|
+
|
|
11
|
+
export class Animation2DStateMachineActionRuntimeHandle {
|
|
12
|
+
readonly duration: number;
|
|
13
|
+
readonly clipId: string;
|
|
14
|
+
readonly action: Animation2DActionRuntime;
|
|
15
|
+
|
|
16
|
+
targetTime = 0;
|
|
17
|
+
playing = false;
|
|
18
|
+
destroyed = false;
|
|
19
|
+
pendingDestroy = false;
|
|
20
|
+
|
|
21
|
+
private readonly _checkpoint: Animation2DActionSynchronizedState;
|
|
22
|
+
private _checkpointTargetTime = 0;
|
|
23
|
+
private _checkpointPlaying = false;
|
|
24
|
+
|
|
25
|
+
constructor(clipId: string, action: Animation2DActionRuntime) {
|
|
26
|
+
this.duration = action.duration;
|
|
27
|
+
this.clipId = clipId;
|
|
28
|
+
this.action = action;
|
|
29
|
+
this._checkpoint = action.createSynchronizedState();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
captureTransactionState(): void {
|
|
33
|
+
this._checkpointTargetTime = this.targetTime;
|
|
34
|
+
this._checkpointPlaying = this.playing;
|
|
35
|
+
this.action.captureSynchronizedState(this._checkpoint);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
restoreTransactionState(): void {
|
|
39
|
+
this.targetTime = this._checkpointTargetTime;
|
|
40
|
+
this.playing = this._checkpointPlaying;
|
|
41
|
+
this.pendingDestroy = false;
|
|
42
|
+
this.action.restoreSynchronizedState(this._checkpoint);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Transactional state-machine owner for the existing 2D mixer. It never
|
|
48
|
+
* samples tracks itself: absolute controller playheads are synchronized into
|
|
49
|
+
* the mixer actions, and the caller evaluates the ordinary shared pose buffer.
|
|
50
|
+
*/
|
|
51
|
+
export class Animation2DStateMachineMixerAdapter implements
|
|
52
|
+
AnimationStateMachineMixerPort<Animation2DStateMachineActionRuntimeHandle> {
|
|
53
|
+
private readonly _handles = new Set<Animation2DStateMachineActionRuntimeHandle>();
|
|
54
|
+
private readonly _transactionHandles: Animation2DStateMachineActionRuntimeHandle[] = [];
|
|
55
|
+
private readonly _transactionCreated: Animation2DStateMachineActionRuntimeHandle[] = [];
|
|
56
|
+
private readonly _transactionPendingDestroy: Animation2DStateMachineActionRuntimeHandle[] = [];
|
|
57
|
+
private _transactionActive = false;
|
|
58
|
+
private _destroyed = false;
|
|
59
|
+
|
|
60
|
+
constructor(readonly mixer: Animation2DMixerRuntime) {}
|
|
61
|
+
|
|
62
|
+
get liveActionCount(): number { return this._handles.size; }
|
|
63
|
+
get liveBindingCount(): number { return this.mixer.liveBindingCount; }
|
|
64
|
+
|
|
65
|
+
createAction(
|
|
66
|
+
clipId: string,
|
|
67
|
+
options: AnimationStateMachineActionOptions,
|
|
68
|
+
): Animation2DStateMachineActionRuntimeHandle {
|
|
69
|
+
this._requireActive();
|
|
70
|
+
const handle = new Animation2DStateMachineActionRuntimeHandle(
|
|
71
|
+
clipId,
|
|
72
|
+
this.mixer.createAction(clipId, options),
|
|
73
|
+
);
|
|
74
|
+
this._handles.add(handle);
|
|
75
|
+
if (this._transactionActive) this._transactionCreated.push(handle);
|
|
76
|
+
return handle;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
play(handle: Animation2DStateMachineActionRuntimeHandle): void {
|
|
80
|
+
this._requireHandle(handle);
|
|
81
|
+
this.mixer.play(handle.action);
|
|
82
|
+
handle.playing = true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
stop(handle: Animation2DStateMachineActionRuntimeHandle): void {
|
|
86
|
+
this._requireHandle(handle);
|
|
87
|
+
this.mixer.stop(handle.action);
|
|
88
|
+
handle.playing = false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
fade(
|
|
92
|
+
handle: Animation2DStateMachineActionRuntimeHandle,
|
|
93
|
+
_targetWeight: number,
|
|
94
|
+
_durationSeconds: number,
|
|
95
|
+
): void {
|
|
96
|
+
this._requireHandle(handle);
|
|
97
|
+
// The controller is the unique transition clock and applies exact weights.
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
setWeight(handle: Animation2DStateMachineActionRuntimeHandle, weight: number): void {
|
|
101
|
+
this._requireHandle(handle);
|
|
102
|
+
this.mixer.setWeight(handle.action, weight);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
setTime(handle: Animation2DStateMachineActionRuntimeHandle, timeSeconds: number): void {
|
|
106
|
+
this._requireHandle(handle);
|
|
107
|
+
handle.targetTime = timeSeconds;
|
|
108
|
+
this.mixer.setTime(handle.action, timeSeconds);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
setTimeScale(handle: Animation2DStateMachineActionRuntimeHandle, timeScale: number): void {
|
|
112
|
+
this._requireHandle(handle);
|
|
113
|
+
this.mixer.setTimeScale(handle.action, timeScale);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
destroyAction(handle: Animation2DStateMachineActionRuntimeHandle): void {
|
|
117
|
+
if (handle.destroyed) return;
|
|
118
|
+
this._requireHandle(handle);
|
|
119
|
+
if (this._transactionActive) {
|
|
120
|
+
if (!handle.pendingDestroy) {
|
|
121
|
+
handle.pendingDestroy = true;
|
|
122
|
+
this._transactionPendingDestroy.push(handle);
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
this._destroyHandleNow(handle);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
beginControllerTransaction(): void {
|
|
130
|
+
this._requireActive();
|
|
131
|
+
if (this._transactionActive) {
|
|
132
|
+
throw new Error('Animation2D state-machine adapter transaction is already active.');
|
|
133
|
+
}
|
|
134
|
+
this._transactionActive = true;
|
|
135
|
+
this._transactionHandles.length = 0;
|
|
136
|
+
this._transactionCreated.length = 0;
|
|
137
|
+
this._transactionPendingDestroy.length = 0;
|
|
138
|
+
for (const handle of this._handles) {
|
|
139
|
+
handle.captureTransactionState();
|
|
140
|
+
this._transactionHandles.push(handle);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
commitControllerTransaction(): void {
|
|
145
|
+
if (!this._transactionActive) {
|
|
146
|
+
throw new Error('Animation2D state-machine adapter has no active transaction.');
|
|
147
|
+
}
|
|
148
|
+
this._transactionActive = false;
|
|
149
|
+
for (const handle of this._transactionPendingDestroy) this._destroyHandleNow(handle);
|
|
150
|
+
this._clearTransaction();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
rollbackControllerTransaction(): void {
|
|
154
|
+
if (!this._transactionActive) return;
|
|
155
|
+
this._transactionActive = false;
|
|
156
|
+
for (let index = this._transactionCreated.length - 1; index >= 0; index--) {
|
|
157
|
+
this._destroyHandleNow(this._transactionCreated[index]!);
|
|
158
|
+
}
|
|
159
|
+
for (const handle of this._transactionHandles) {
|
|
160
|
+
if (!handle.destroyed) handle.restoreTransactionState();
|
|
161
|
+
}
|
|
162
|
+
this._clearTransaction();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
destroy(): void {
|
|
166
|
+
if (this._destroyed) return;
|
|
167
|
+
this.rollbackControllerTransaction();
|
|
168
|
+
for (const handle of [...this._handles]) this._destroyHandleNow(handle);
|
|
169
|
+
this._destroyed = true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private _destroyHandleNow(handle: Animation2DStateMachineActionRuntimeHandle): void {
|
|
173
|
+
if (handle.destroyed) return;
|
|
174
|
+
this.mixer.removeAction(handle.action);
|
|
175
|
+
handle.playing = false;
|
|
176
|
+
handle.pendingDestroy = false;
|
|
177
|
+
handle.destroyed = true;
|
|
178
|
+
this._handles.delete(handle);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
private _clearTransaction(): void {
|
|
182
|
+
this._transactionHandles.length = 0;
|
|
183
|
+
this._transactionCreated.length = 0;
|
|
184
|
+
this._transactionPendingDestroy.length = 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
private _requireHandle(handle: Animation2DStateMachineActionRuntimeHandle): void {
|
|
188
|
+
this._requireActive();
|
|
189
|
+
if (handle.destroyed || !this._handles.has(handle)) {
|
|
190
|
+
throw new Error('Animation2D state-machine action belongs to another adapter or was destroyed.');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private _requireActive(): void {
|
|
195
|
+
if (this._destroyed) throw new Error('Animation2D state-machine adapter has been destroyed.');
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CompiledAnimation3DBlend1DMotion,
|
|
3
|
+
CompiledAnimation3DBlend2DMotion,
|
|
4
|
+
} from './AnimationStateMachineCompiler.js';
|
|
5
|
+
|
|
6
|
+
const EPSILON = 1e-9;
|
|
7
|
+
const TWO_PI = Math.PI * 2;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Evaluates direct child weights for a compiled 1D blend tree. Values outside
|
|
11
|
+
* the authored threshold range clamp to the nearest endpoint.
|
|
12
|
+
*/
|
|
13
|
+
export function evaluateAnimation3DBlend1DWeights(
|
|
14
|
+
motion: CompiledAnimation3DBlend1DMotion,
|
|
15
|
+
value: number,
|
|
16
|
+
out: Float64Array = new Float64Array(motion.children.length),
|
|
17
|
+
): Float64Array {
|
|
18
|
+
requireOutputSize(out, motion.children.length);
|
|
19
|
+
out.fill(0);
|
|
20
|
+
const children = motion.children;
|
|
21
|
+
if (children.length === 1 || value <= children[0]!.threshold) {
|
|
22
|
+
out[0] = 1;
|
|
23
|
+
return out;
|
|
24
|
+
}
|
|
25
|
+
const lastIndex = children.length - 1;
|
|
26
|
+
if (value >= children[lastIndex]!.threshold) {
|
|
27
|
+
out[lastIndex] = 1;
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
let low = 0;
|
|
31
|
+
let high = lastIndex;
|
|
32
|
+
while (low + 1 < high) {
|
|
33
|
+
const middle = (low + high) >>> 1;
|
|
34
|
+
if (value < children[middle]!.threshold) high = middle;
|
|
35
|
+
else low = middle;
|
|
36
|
+
}
|
|
37
|
+
const start = children[low]!.threshold;
|
|
38
|
+
const end = children[high]!.threshold;
|
|
39
|
+
const alpha = (value - start) / (end - start);
|
|
40
|
+
out[low] = 1 - alpha;
|
|
41
|
+
out[high] = alpha;
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Evaluates direct child weights for a compiled 2D blend tree.
|
|
47
|
+
*
|
|
48
|
+
* Cartesian trees use a deterministic containing simplex after projecting
|
|
49
|
+
* outside samples to the convex hull. Directional trees interpolate the two
|
|
50
|
+
* surrounding authored directions and then interpolate/clamp radially.
|
|
51
|
+
*/
|
|
52
|
+
export function evaluateAnimation3DBlend2DWeights(
|
|
53
|
+
motion: CompiledAnimation3DBlend2DMotion,
|
|
54
|
+
x: number,
|
|
55
|
+
y: number,
|
|
56
|
+
out: Float64Array = new Float64Array(motion.children.length),
|
|
57
|
+
): Float64Array {
|
|
58
|
+
requireOutputSize(out, motion.children.length);
|
|
59
|
+
out.fill(0);
|
|
60
|
+
if (motion.children.length === 1) {
|
|
61
|
+
out[0] = 1;
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
return motion.algorithm === 'directional'
|
|
65
|
+
? evaluateDirectional(motion, x, y, out)
|
|
66
|
+
: evaluateCartesian(motion, x, y, out);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function evaluateCartesian(
|
|
70
|
+
motion: CompiledAnimation3DBlend2DMotion,
|
|
71
|
+
x: number,
|
|
72
|
+
y: number,
|
|
73
|
+
out: Float64Array,
|
|
74
|
+
): Float64Array {
|
|
75
|
+
const children = motion.children;
|
|
76
|
+
const exact = findExactPoint(motion, x, y);
|
|
77
|
+
if (exact >= 0) {
|
|
78
|
+
out[exact] = 1;
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const hull = convexHull(motion);
|
|
83
|
+
const point = clampToHull(motion, hull, x, y);
|
|
84
|
+
const clampedExact = findExactPoint(motion, point[0], point[1]);
|
|
85
|
+
if (clampedExact >= 0) {
|
|
86
|
+
out[clampedExact] = 1;
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let bestArea = Infinity;
|
|
91
|
+
let bestIndices: readonly [number, number, number] | null = null;
|
|
92
|
+
let bestWeights: readonly [number, number, number] | null = null;
|
|
93
|
+
for (let first = 0; first < children.length - 2; first++) {
|
|
94
|
+
for (let second = first + 1; second < children.length - 1; second++) {
|
|
95
|
+
for (let third = second + 1; third < children.length; third++) {
|
|
96
|
+
const a = children[first]!.position;
|
|
97
|
+
const b = children[second]!.position;
|
|
98
|
+
const c = children[third]!.position;
|
|
99
|
+
const signedDoubleArea = cross(a, b, c);
|
|
100
|
+
const area = Math.abs(signedDoubleArea);
|
|
101
|
+
if (area <= EPSILON) continue;
|
|
102
|
+
const weights = barycentric(point, a, b, c, signedDoubleArea);
|
|
103
|
+
if (
|
|
104
|
+
weights[0] < -EPSILON
|
|
105
|
+
|| weights[1] < -EPSILON
|
|
106
|
+
|| weights[2] < -EPSILON
|
|
107
|
+
) continue;
|
|
108
|
+
if (
|
|
109
|
+
area < bestArea - EPSILON
|
|
110
|
+
|| (
|
|
111
|
+
Math.abs(area - bestArea) <= EPSILON
|
|
112
|
+
&& lexicographicallyBefore(
|
|
113
|
+
[first, second, third],
|
|
114
|
+
bestIndices,
|
|
115
|
+
)
|
|
116
|
+
)
|
|
117
|
+
) {
|
|
118
|
+
bestArea = area;
|
|
119
|
+
bestIndices = [first, second, third];
|
|
120
|
+
bestWeights = weights;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (bestIndices && bestWeights) {
|
|
126
|
+
out[bestIndices[0]] = clamp01(bestWeights[0]);
|
|
127
|
+
out[bestIndices[1]] = clamp01(bestWeights[1]);
|
|
128
|
+
out[bestIndices[2]] = clamp01(bestWeights[2]);
|
|
129
|
+
normalize(out);
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Collinear input (or a numerically degenerate hull): clamp to the closest
|
|
134
|
+
// authored segment with declaration order as the final tie breaker.
|
|
135
|
+
let bestDistance = Infinity;
|
|
136
|
+
let bestLength = Infinity;
|
|
137
|
+
let bestFirst = 0;
|
|
138
|
+
let bestSecond = 1;
|
|
139
|
+
let bestAlpha = 0;
|
|
140
|
+
for (let first = 0; first < children.length - 1; first++) {
|
|
141
|
+
for (let second = first + 1; second < children.length; second++) {
|
|
142
|
+
const projection = closestPointOnSegment(
|
|
143
|
+
point[0],
|
|
144
|
+
point[1],
|
|
145
|
+
children[first]!.position,
|
|
146
|
+
children[second]!.position,
|
|
147
|
+
);
|
|
148
|
+
const dx = projection[0] - point[0];
|
|
149
|
+
const dy = projection[1] - point[1];
|
|
150
|
+
const distance = dx * dx + dy * dy;
|
|
151
|
+
const segmentDx =
|
|
152
|
+
children[second]!.position[0] - children[first]!.position[0];
|
|
153
|
+
const segmentDy =
|
|
154
|
+
children[second]!.position[1] - children[first]!.position[1];
|
|
155
|
+
const length = segmentDx * segmentDx + segmentDy * segmentDy;
|
|
156
|
+
if (
|
|
157
|
+
distance < bestDistance - EPSILON
|
|
158
|
+
|| (
|
|
159
|
+
Math.abs(distance - bestDistance) <= EPSILON
|
|
160
|
+
&& length < bestLength - EPSILON
|
|
161
|
+
)
|
|
162
|
+
) {
|
|
163
|
+
bestDistance = distance;
|
|
164
|
+
bestLength = length;
|
|
165
|
+
bestFirst = first;
|
|
166
|
+
bestSecond = second;
|
|
167
|
+
bestAlpha = projection[2];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
out[bestFirst] = 1 - bestAlpha;
|
|
172
|
+
out[bestSecond] = bestAlpha;
|
|
173
|
+
return out;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
interface DirectionGroup {
|
|
177
|
+
readonly angle: number;
|
|
178
|
+
readonly entries: readonly {
|
|
179
|
+
readonly index: number;
|
|
180
|
+
readonly radius: number;
|
|
181
|
+
}[];
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function evaluateDirectional(
|
|
185
|
+
motion: CompiledAnimation3DBlend2DMotion,
|
|
186
|
+
x: number,
|
|
187
|
+
y: number,
|
|
188
|
+
out: Float64Array,
|
|
189
|
+
): Float64Array {
|
|
190
|
+
const exact = findExactPoint(motion, x, y);
|
|
191
|
+
if (exact >= 0) {
|
|
192
|
+
out[exact] = 1;
|
|
193
|
+
return out;
|
|
194
|
+
}
|
|
195
|
+
const radius = Math.hypot(x, y);
|
|
196
|
+
const originIndices: number[] = [];
|
|
197
|
+
const directionalEntries: {
|
|
198
|
+
angle: number;
|
|
199
|
+
radius: number;
|
|
200
|
+
index: number;
|
|
201
|
+
}[] = [];
|
|
202
|
+
motion.children.forEach((child, index) => {
|
|
203
|
+
const childRadius = Math.hypot(child.position[0], child.position[1]);
|
|
204
|
+
if (childRadius <= EPSILON) {
|
|
205
|
+
originIndices.push(index);
|
|
206
|
+
} else {
|
|
207
|
+
directionalEntries.push({
|
|
208
|
+
angle: normalizeAngle(Math.atan2(child.position[1], child.position[0])),
|
|
209
|
+
radius: childRadius,
|
|
210
|
+
index,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
if (radius <= EPSILON) {
|
|
215
|
+
out[originIndices[0] ?? directionalEntries[0]!.index] = 1;
|
|
216
|
+
return out;
|
|
217
|
+
}
|
|
218
|
+
if (directionalEntries.length === 0) {
|
|
219
|
+
out[originIndices[0]!] = 1;
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
directionalEntries.sort((left, right) =>
|
|
223
|
+
left.angle - right.angle
|
|
224
|
+
|| left.radius - right.radius
|
|
225
|
+
|| left.index - right.index);
|
|
226
|
+
const groups: DirectionGroup[] = [];
|
|
227
|
+
directionalEntries.forEach(entry => {
|
|
228
|
+
const previous = groups[groups.length - 1];
|
|
229
|
+
if (previous && Math.abs(previous.angle - entry.angle) <= EPSILON) {
|
|
230
|
+
groups[groups.length - 1] = {
|
|
231
|
+
angle: previous.angle,
|
|
232
|
+
entries: Object.freeze([...previous.entries, {
|
|
233
|
+
index: entry.index,
|
|
234
|
+
radius: entry.radius,
|
|
235
|
+
}]),
|
|
236
|
+
};
|
|
237
|
+
} else {
|
|
238
|
+
groups.push({
|
|
239
|
+
angle: entry.angle,
|
|
240
|
+
entries: Object.freeze([{
|
|
241
|
+
index: entry.index,
|
|
242
|
+
radius: entry.radius,
|
|
243
|
+
}]),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const queryAngle = normalizeAngle(Math.atan2(y, x));
|
|
249
|
+
let leftGroupIndex = groups.length - 1;
|
|
250
|
+
let rightGroupIndex = 0;
|
|
251
|
+
let angularAlpha = 0;
|
|
252
|
+
for (let index = 0; index < groups.length; index++) {
|
|
253
|
+
const left = groups[index]!;
|
|
254
|
+
const rightIndex = (index + 1) % groups.length;
|
|
255
|
+
const right = groups[rightIndex]!;
|
|
256
|
+
const leftAngle = left.angle;
|
|
257
|
+
const rightAngle = rightIndex === 0 ? right.angle + TWO_PI : right.angle;
|
|
258
|
+
const adjustedQuery = queryAngle < leftAngle ? queryAngle + TWO_PI : queryAngle;
|
|
259
|
+
if (adjustedQuery >= leftAngle - EPSILON && adjustedQuery <= rightAngle + EPSILON) {
|
|
260
|
+
leftGroupIndex = index;
|
|
261
|
+
rightGroupIndex = rightIndex;
|
|
262
|
+
angularAlpha = groups.length === 1
|
|
263
|
+
? 0
|
|
264
|
+
: clamp01((adjustedQuery - leftAngle) / (rightAngle - leftAngle));
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const originIndex = originIndices[0] ?? -1;
|
|
270
|
+
accumulateRadial(
|
|
271
|
+
groups[leftGroupIndex]!,
|
|
272
|
+
radius,
|
|
273
|
+
1 - angularAlpha,
|
|
274
|
+
originIndex,
|
|
275
|
+
out,
|
|
276
|
+
);
|
|
277
|
+
if (rightGroupIndex !== leftGroupIndex) {
|
|
278
|
+
accumulateRadial(
|
|
279
|
+
groups[rightGroupIndex]!,
|
|
280
|
+
radius,
|
|
281
|
+
angularAlpha,
|
|
282
|
+
originIndex,
|
|
283
|
+
out,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
normalize(out);
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function accumulateRadial(
|
|
291
|
+
group: DirectionGroup,
|
|
292
|
+
radius: number,
|
|
293
|
+
angularWeight: number,
|
|
294
|
+
originIndex: number,
|
|
295
|
+
out: Float64Array,
|
|
296
|
+
): void {
|
|
297
|
+
if (angularWeight <= EPSILON) return;
|
|
298
|
+
const entries = group.entries;
|
|
299
|
+
const first = entries[0]!;
|
|
300
|
+
if (radius <= first.radius) {
|
|
301
|
+
if (originIndex >= 0) {
|
|
302
|
+
const alpha = clamp01(radius / first.radius);
|
|
303
|
+
out[originIndex] = (out[originIndex] ?? 0) + angularWeight * (1 - alpha);
|
|
304
|
+
out[first.index] = (out[first.index] ?? 0) + angularWeight * alpha;
|
|
305
|
+
} else {
|
|
306
|
+
out[first.index] = (out[first.index] ?? 0) + angularWeight;
|
|
307
|
+
}
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const last = entries[entries.length - 1]!;
|
|
311
|
+
if (radius >= last.radius) {
|
|
312
|
+
out[last.index] = (out[last.index] ?? 0) + angularWeight;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
for (let index = 0; index < entries.length - 1; index++) {
|
|
316
|
+
const start = entries[index]!;
|
|
317
|
+
const end = entries[index + 1]!;
|
|
318
|
+
if (radius <= end.radius) {
|
|
319
|
+
const alpha = (radius - start.radius) / (end.radius - start.radius);
|
|
320
|
+
out[start.index] = (out[start.index] ?? 0) + angularWeight * (1 - alpha);
|
|
321
|
+
out[end.index] = (out[end.index] ?? 0) + angularWeight * alpha;
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function convexHull(motion: CompiledAnimation3DBlend2DMotion): readonly number[] {
|
|
328
|
+
const sorted = motion.children.map((child, index) => ({
|
|
329
|
+
index,
|
|
330
|
+
x: child.position[0],
|
|
331
|
+
y: child.position[1],
|
|
332
|
+
})).sort((left, right) =>
|
|
333
|
+
left.x - right.x || left.y - right.y || left.index - right.index);
|
|
334
|
+
if (sorted.length <= 2) return sorted.map(entry => entry.index);
|
|
335
|
+
const lower: typeof sorted = [];
|
|
336
|
+
for (const entry of sorted) {
|
|
337
|
+
while (
|
|
338
|
+
lower.length >= 2
|
|
339
|
+
&& crossEntries(lower[lower.length - 2]!, lower[lower.length - 1]!, entry) <= EPSILON
|
|
340
|
+
) lower.pop();
|
|
341
|
+
lower.push(entry);
|
|
342
|
+
}
|
|
343
|
+
const upper: typeof sorted = [];
|
|
344
|
+
for (let index = sorted.length - 1; index >= 0; index--) {
|
|
345
|
+
const entry = sorted[index]!;
|
|
346
|
+
while (
|
|
347
|
+
upper.length >= 2
|
|
348
|
+
&& crossEntries(upper[upper.length - 2]!, upper[upper.length - 1]!, entry) <= EPSILON
|
|
349
|
+
) upper.pop();
|
|
350
|
+
upper.push(entry);
|
|
351
|
+
}
|
|
352
|
+
lower.pop();
|
|
353
|
+
upper.pop();
|
|
354
|
+
const result = [...lower, ...upper].map(entry => entry.index);
|
|
355
|
+
return result.length === 0 ? [sorted[0]!.index] : result;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function clampToHull(
|
|
359
|
+
motion: CompiledAnimation3DBlend2DMotion,
|
|
360
|
+
hull: readonly number[],
|
|
361
|
+
x: number,
|
|
362
|
+
y: number,
|
|
363
|
+
): readonly [number, number] {
|
|
364
|
+
if (hull.length === 1) return motion.children[hull[0]!]!.position;
|
|
365
|
+
if (hull.length === 2) {
|
|
366
|
+
const projection = closestPointOnSegment(
|
|
367
|
+
x,
|
|
368
|
+
y,
|
|
369
|
+
motion.children[hull[0]!]!.position,
|
|
370
|
+
motion.children[hull[1]!]!.position,
|
|
371
|
+
);
|
|
372
|
+
return [projection[0], projection[1]];
|
|
373
|
+
}
|
|
374
|
+
let inside = true;
|
|
375
|
+
for (let index = 0; index < hull.length; index++) {
|
|
376
|
+
const start = motion.children[hull[index]!]!.position;
|
|
377
|
+
const end = motion.children[hull[(index + 1) % hull.length]!]!.position;
|
|
378
|
+
if ((end[0] - start[0]) * (y - start[1]) - (end[1] - start[1]) * (x - start[0]) < -EPSILON) {
|
|
379
|
+
inside = false;
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
if (inside) return [x, y];
|
|
384
|
+
|
|
385
|
+
let bestX = x;
|
|
386
|
+
let bestY = y;
|
|
387
|
+
let bestDistance = Infinity;
|
|
388
|
+
for (let index = 0; index < hull.length; index++) {
|
|
389
|
+
const projection = closestPointOnSegment(
|
|
390
|
+
x,
|
|
391
|
+
y,
|
|
392
|
+
motion.children[hull[index]!]!.position,
|
|
393
|
+
motion.children[hull[(index + 1) % hull.length]!]!.position,
|
|
394
|
+
);
|
|
395
|
+
const dx = projection[0] - x;
|
|
396
|
+
const dy = projection[1] - y;
|
|
397
|
+
const distance = dx * dx + dy * dy;
|
|
398
|
+
if (distance < bestDistance - EPSILON) {
|
|
399
|
+
bestDistance = distance;
|
|
400
|
+
bestX = projection[0];
|
|
401
|
+
bestY = projection[1];
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return [bestX, bestY];
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
function closestPointOnSegment(
|
|
408
|
+
x: number,
|
|
409
|
+
y: number,
|
|
410
|
+
start: readonly [number, number],
|
|
411
|
+
end: readonly [number, number],
|
|
412
|
+
): readonly [number, number, number] {
|
|
413
|
+
const dx = end[0] - start[0];
|
|
414
|
+
const dy = end[1] - start[1];
|
|
415
|
+
const denominator = dx * dx + dy * dy;
|
|
416
|
+
const alpha = denominator <= EPSILON
|
|
417
|
+
? 0
|
|
418
|
+
: clamp01(((x - start[0]) * dx + (y - start[1]) * dy) / denominator);
|
|
419
|
+
return [start[0] + dx * alpha, start[1] + dy * alpha, alpha];
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function barycentric(
|
|
423
|
+
point: readonly [number, number],
|
|
424
|
+
a: readonly [number, number],
|
|
425
|
+
b: readonly [number, number],
|
|
426
|
+
c: readonly [number, number],
|
|
427
|
+
signedDoubleArea: number,
|
|
428
|
+
): readonly [number, number, number] {
|
|
429
|
+
const first = (
|
|
430
|
+
(b[0] - point[0]) * (c[1] - point[1])
|
|
431
|
+
- (b[1] - point[1]) * (c[0] - point[0])
|
|
432
|
+
) / signedDoubleArea;
|
|
433
|
+
const second = (
|
|
434
|
+
(c[0] - point[0]) * (a[1] - point[1])
|
|
435
|
+
- (c[1] - point[1]) * (a[0] - point[0])
|
|
436
|
+
) / signedDoubleArea;
|
|
437
|
+
return [first, second, 1 - first - second];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function findExactPoint(
|
|
441
|
+
motion: CompiledAnimation3DBlend2DMotion,
|
|
442
|
+
x: number,
|
|
443
|
+
y: number,
|
|
444
|
+
): number {
|
|
445
|
+
for (let index = 0; index < motion.children.length; index++) {
|
|
446
|
+
const position = motion.children[index]!.position;
|
|
447
|
+
if (Math.abs(position[0] - x) <= EPSILON && Math.abs(position[1] - y) <= EPSILON) {
|
|
448
|
+
return index;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return -1;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
function cross(
|
|
455
|
+
a: readonly [number, number],
|
|
456
|
+
b: readonly [number, number],
|
|
457
|
+
c: readonly [number, number],
|
|
458
|
+
): number {
|
|
459
|
+
return (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function crossEntries(
|
|
463
|
+
a: { readonly x: number; readonly y: number },
|
|
464
|
+
b: { readonly x: number; readonly y: number },
|
|
465
|
+
c: { readonly x: number; readonly y: number },
|
|
466
|
+
): number {
|
|
467
|
+
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
function lexicographicallyBefore(
|
|
471
|
+
candidate: readonly [number, number, number],
|
|
472
|
+
current: readonly [number, number, number] | null,
|
|
473
|
+
): boolean {
|
|
474
|
+
if (!current) return true;
|
|
475
|
+
return candidate[0] < current[0]
|
|
476
|
+
|| (candidate[0] === current[0] && candidate[1] < current[1])
|
|
477
|
+
|| (
|
|
478
|
+
candidate[0] === current[0]
|
|
479
|
+
&& candidate[1] === current[1]
|
|
480
|
+
&& candidate[2] < current[2]
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function normalize(out: Float64Array): void {
|
|
485
|
+
let total = 0;
|
|
486
|
+
for (const value of out) total += value;
|
|
487
|
+
if (total <= EPSILON) return;
|
|
488
|
+
for (let index = 0; index < out.length; index++) out[index] = out[index]! / total;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function normalizeAngle(angle: number): number {
|
|
492
|
+
return angle < 0 ? angle + TWO_PI : angle;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
function clamp01(value: number): number {
|
|
496
|
+
return Math.max(0, Math.min(1, value));
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function requireOutputSize(out: Float64Array, size: number): void {
|
|
500
|
+
if (out.length < size) {
|
|
501
|
+
throw new RangeError(`Blend weight output requires ${size} entries; received ${out.length}.`);
|
|
502
|
+
}
|
|
503
|
+
}
|