@zephyr3d/editor 0.3.4 → 0.3.7
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/dist/assets/{index-DzQzWsCG.js → index-ySiLtSxz.js} +3 -3
- package/dist/assistant/zephyr-types-index.json +14217 -7895
- package/dist/index.html +1 -1
- package/dist/modules/zephyr3d_backend-webgl.js.map +1 -1
- package/dist/modules/zephyr3d_backend-webgpu.js +13 -12
- package/dist/modules/zephyr3d_backend-webgpu.js.map +1 -1
- package/dist/modules/zephyr3d_device.js +15 -15
- package/dist/modules/zephyr3d_device.js.map +1 -1
- package/dist/modules/zephyr3d_imgui.js.map +1 -1
- package/dist/modules/zephyr3d_scene.js +3276 -1089
- package/dist/modules/zephyr3d_scene.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgl/dist/index.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/capabilities_webgpu.js +7 -6
- package/dist/vendor/zephyr3d/backend-webgpu/dist/capabilities_webgpu.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/index.js.map +1 -1
- package/dist/vendor/zephyr3d/backend-webgpu/dist/renderpass_webgpu.js +6 -6
- package/dist/vendor/zephyr3d/backend-webgpu/dist/renderpass_webgpu.js.map +1 -1
- package/dist/vendor/zephyr3d/device/dist/device.js +15 -15
- package/dist/vendor/zephyr3d/device/dist/device.js.map +1 -1
- package/dist/vendor/zephyr3d/imgui/dist/index.d.ts +7 -0
- package/dist/vendor/zephyr3d/imgui/dist/index.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/animation/animation.js +87 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/animation.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/animation/animationcontroller.js +503 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/animationcontroller.js.map +1 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js +686 -63
- package/dist/vendor/zephyr3d/scene/dist/animation/animationset.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/animation/animationtimeline.js +974 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/animationtimeline.js.map +1 -0
- package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics_modifier.js +19 -19
- package/dist/vendor/zephyr3d/scene/dist/animation/joint_dynamics_modifier.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/app/engine.js +131 -131
- package/dist/vendor/zephyr3d/scene/dist/app/engine.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/app/scriptregistry.js +17 -12
- package/dist/vendor/zephyr3d/scene/dist/app/scriptregistry.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/asset/model.js +2 -1
- package/dist/vendor/zephyr3d/scene/dist/asset/model.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/avatar/wardrobe.js +13 -13
- package/dist/vendor/zephyr3d/scene/dist/avatar/wardrobe.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/camera/camera.js +201 -201
- package/dist/vendor/zephyr3d/scene/dist/camera/camera.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/index.d.ts +1271 -10
- package/dist/vendor/zephyr3d/scene/dist/index.js +3 -1
- package/dist/vendor/zephyr3d/scene/dist/index.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/material/mixins/lightmodel/pbrmetallicroughness.js +5 -5
- package/dist/vendor/zephyr3d/scene/dist/material/mixins/lightmodel/pbrmetallicroughness.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/material/mixins/lit.js +7 -3
- package/dist/vendor/zephyr3d/scene/dist/material/mixins/lit.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/material/mtoon.js +4 -4
- package/dist/vendor/zephyr3d/scene/dist/material/mtoon.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/material/pbrblueprint.js +11 -11
- package/dist/vendor/zephyr3d/scene/dist/material/pbrblueprint.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/material/shader/helper.js +284 -284
- package/dist/vendor/zephyr3d/scene/dist/material/shader/helper.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/posteffect/ssr.js +9 -9
- package/dist/vendor/zephyr3d/scene/dist/posteffect/ssr.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/render/lightpass.js +5 -5
- package/dist/vendor/zephyr3d/scene/dist/render/lightpass.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/render/renderer.js +7 -7
- package/dist/vendor/zephyr3d/scene/dist/render/renderer.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/shadow/esm.js +1 -1
- package/dist/vendor/zephyr3d/scene/dist/shadow/esm.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/shadow/shadow_region.js +45 -45
- package/dist/vendor/zephyr3d/scene/dist/shadow/shadow_region.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js +276 -275
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/manager.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js +4 -3
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/animation.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/camera.js +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/camera.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/mesh.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/node.js +3 -2
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/node.js.map +1 -1
- package/dist/vendor/zephyr3d/scene/dist/utility/serialization/scene/sprite.js.map +1 -1
- package/package.json +8 -8
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Disposable, weightedAverage, Quaternion, Interpolator, Vector3 } from '@zephyr3d/base';
|
|
1
|
+
import { makeObservable, Disposable, weightedAverage, Quaternion, Observable, Interpolator, Vector3 } from '@zephyr3d/base';
|
|
2
2
|
import { AnimationClip } from './animation.js';
|
|
3
3
|
import { NodeRotationTrack } from './rotationtrack.js';
|
|
4
4
|
import { NodeEulerRotationTrack } from './eulerrotationtrack.js';
|
|
@@ -7,6 +7,224 @@ import { NodeScaleTrack } from './scaletrack.js';
|
|
|
7
7
|
import { HumanoidBodyRig } from './skeleton.js';
|
|
8
8
|
import { createSkeletalMaskedAnimationClip } from './animationmask.js';
|
|
9
9
|
|
|
10
|
+
let nextAnimationPlaybackId = 1;
|
|
11
|
+
/**
|
|
12
|
+
* Runtime handle for one playback of an AnimationClip.
|
|
13
|
+
* @public
|
|
14
|
+
*/ class AnimationPlayback extends Observable {
|
|
15
|
+
/** @internal */ _animationSet;
|
|
16
|
+
/** @internal */ _clip;
|
|
17
|
+
/** @internal */ _options;
|
|
18
|
+
/** @internal */ _id;
|
|
19
|
+
/** @internal */ _frameWaiters;
|
|
20
|
+
/** @internal */ _state;
|
|
21
|
+
/** @internal */ _time;
|
|
22
|
+
/** @internal */ _weight;
|
|
23
|
+
/** @internal */ _speedRatio;
|
|
24
|
+
/** @internal */ _layer;
|
|
25
|
+
/** @internal */ _priority;
|
|
26
|
+
/** @internal */ _interruptible;
|
|
27
|
+
constructor(animationSet, clip, options){
|
|
28
|
+
super();
|
|
29
|
+
this._animationSet = animationSet;
|
|
30
|
+
this._clip = clip;
|
|
31
|
+
this._options = {
|
|
32
|
+
...options ?? {}
|
|
33
|
+
};
|
|
34
|
+
this._id = this._options.id ?? `animation-playback-${nextAnimationPlaybackId++}`;
|
|
35
|
+
this._state = 'scheduled';
|
|
36
|
+
this._speedRatio = this._options.speedRatio ?? 1;
|
|
37
|
+
this._weight = this._options.weight ?? clip.weight ?? 1;
|
|
38
|
+
this._time = this._speedRatio < 0 ? clip.timeDuration : 0;
|
|
39
|
+
this._layer = this._options.layer ?? 'default';
|
|
40
|
+
this._priority = this._options.priority ?? 0;
|
|
41
|
+
this._interruptible = this._options.interruptible ?? true;
|
|
42
|
+
this._frameWaiters = new Map();
|
|
43
|
+
}
|
|
44
|
+
get id() {
|
|
45
|
+
return this._id;
|
|
46
|
+
}
|
|
47
|
+
get animationSet() {
|
|
48
|
+
return this._animationSet;
|
|
49
|
+
}
|
|
50
|
+
get clip() {
|
|
51
|
+
return this._clip;
|
|
52
|
+
}
|
|
53
|
+
get state() {
|
|
54
|
+
return this._state;
|
|
55
|
+
}
|
|
56
|
+
get time() {
|
|
57
|
+
return this._time;
|
|
58
|
+
}
|
|
59
|
+
set time(value) {
|
|
60
|
+
this.seek(value);
|
|
61
|
+
}
|
|
62
|
+
get normalizedTime() {
|
|
63
|
+
return this._clip.timeDuration > 0 ? this._time / this._clip.timeDuration : 0;
|
|
64
|
+
}
|
|
65
|
+
set normalizedTime(value) {
|
|
66
|
+
this.seek(value * this._clip.timeDuration);
|
|
67
|
+
}
|
|
68
|
+
get weight() {
|
|
69
|
+
return this._weight;
|
|
70
|
+
}
|
|
71
|
+
set weight(value) {
|
|
72
|
+
this._animationSet.setPlaybackWeight(this, value);
|
|
73
|
+
}
|
|
74
|
+
get speedRatio() {
|
|
75
|
+
return this._speedRatio;
|
|
76
|
+
}
|
|
77
|
+
set speedRatio(value) {
|
|
78
|
+
this._animationSet.setPlaybackSpeedRatio(this, value);
|
|
79
|
+
}
|
|
80
|
+
get layer() {
|
|
81
|
+
return this._layer;
|
|
82
|
+
}
|
|
83
|
+
get priority() {
|
|
84
|
+
return this._priority;
|
|
85
|
+
}
|
|
86
|
+
get interruptible() {
|
|
87
|
+
return this._interruptible;
|
|
88
|
+
}
|
|
89
|
+
play() {
|
|
90
|
+
this._animationSet.startPlayback(this);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
pause() {
|
|
94
|
+
this._animationSet.pausePlayback(this);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
resume() {
|
|
98
|
+
this._animationSet.resumePlayback(this);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
stop(options) {
|
|
102
|
+
this._animationSet.stopPlayback(this, options);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
seek(time, options) {
|
|
106
|
+
this._animationSet.seekPlayback(this, time, options);
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
fadeTo(weight, duration) {
|
|
110
|
+
this._animationSet.fadePlaybackTo(this, weight, duration);
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
crossFadeTo(name, options) {
|
|
114
|
+
const duration = Math.max(options?.duration ?? 0, 0);
|
|
115
|
+
this.stop({
|
|
116
|
+
fadeOut: duration,
|
|
117
|
+
reason: 'interrupted'
|
|
118
|
+
});
|
|
119
|
+
return this._animationSet.play(name, {
|
|
120
|
+
...options,
|
|
121
|
+
fadeIn: duration
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
waitForComplete() {
|
|
125
|
+
return new Promise((resolve)=>{
|
|
126
|
+
this.once('complete', ()=>resolve(this));
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
waitForMarker(idOrName) {
|
|
130
|
+
if (this._state === 'stopped' || this._state === 'completed') {
|
|
131
|
+
return Promise.resolve(undefined);
|
|
132
|
+
}
|
|
133
|
+
return new Promise((resolve)=>{
|
|
134
|
+
const handler = (event)=>{
|
|
135
|
+
if (event.marker.id === idOrName || event.marker.name === idOrName) {
|
|
136
|
+
this.off('marker', handler);
|
|
137
|
+
this.off('stop', stop);
|
|
138
|
+
resolve(event);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const stop = ()=>{
|
|
142
|
+
this.off('marker', handler);
|
|
143
|
+
this.off('stop', stop);
|
|
144
|
+
resolve(undefined);
|
|
145
|
+
};
|
|
146
|
+
this.on('marker', handler);
|
|
147
|
+
this.on('stop', stop);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
waitForFrame(frame) {
|
|
151
|
+
if (this._state === 'stopped' || this._state === 'completed') {
|
|
152
|
+
return Promise.resolve(undefined);
|
|
153
|
+
}
|
|
154
|
+
return new Promise((resolve)=>{
|
|
155
|
+
const waiters = this._frameWaiters.get(frame) ?? [];
|
|
156
|
+
let settled = false;
|
|
157
|
+
let stop;
|
|
158
|
+
const wrappedResolve = (event)=>{
|
|
159
|
+
if (settled) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
settled = true;
|
|
163
|
+
this.off('stop', stop);
|
|
164
|
+
resolve(event);
|
|
165
|
+
};
|
|
166
|
+
stop = ()=>{
|
|
167
|
+
const current = this._frameWaiters.get(frame);
|
|
168
|
+
if (current) {
|
|
169
|
+
this._frameWaiters.set(frame, current.filter((item)=>item !== wrappedResolve));
|
|
170
|
+
}
|
|
171
|
+
wrappedResolve(undefined);
|
|
172
|
+
};
|
|
173
|
+
waiters.push(wrappedResolve);
|
|
174
|
+
this._frameWaiters.set(frame, waiters);
|
|
175
|
+
this.on('stop', stop);
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/** @internal */ _getOptions() {
|
|
179
|
+
return this._options;
|
|
180
|
+
}
|
|
181
|
+
/** @internal */ _setState(state) {
|
|
182
|
+
this._state = state;
|
|
183
|
+
}
|
|
184
|
+
/** @internal */ _setTime(time) {
|
|
185
|
+
this._time = time;
|
|
186
|
+
}
|
|
187
|
+
/** @internal */ _setWeight(weight) {
|
|
188
|
+
this._weight = weight;
|
|
189
|
+
}
|
|
190
|
+
/** @internal */ _setSpeedRatio(speedRatio) {
|
|
191
|
+
this._speedRatio = speedRatio;
|
|
192
|
+
}
|
|
193
|
+
/** @internal */ _getWatchedFrames() {
|
|
194
|
+
return [
|
|
195
|
+
...this._frameWaiters.keys()
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
/** @internal */ _emitStart(event) {
|
|
199
|
+
this.dispatchEvent('start', event);
|
|
200
|
+
}
|
|
201
|
+
/** @internal */ _emitLoop(event) {
|
|
202
|
+
this.dispatchEvent('loop', event);
|
|
203
|
+
}
|
|
204
|
+
/** @internal */ _emitMarker(event) {
|
|
205
|
+
this.dispatchEvent('marker', event);
|
|
206
|
+
}
|
|
207
|
+
/** @internal */ _emitFrame(event) {
|
|
208
|
+
this.dispatchEvent('frame', event);
|
|
209
|
+
const waiters = this._frameWaiters.get(event.frame);
|
|
210
|
+
if (waiters) {
|
|
211
|
+
this._frameWaiters.delete(event.frame);
|
|
212
|
+
waiters.forEach((resolve)=>resolve(event));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
/** @internal */ _emitComplete(event) {
|
|
216
|
+
this.dispatchEvent('complete', event);
|
|
217
|
+
}
|
|
218
|
+
/** @internal */ _emitStop(event) {
|
|
219
|
+
this.dispatchEvent('stop', event);
|
|
220
|
+
}
|
|
221
|
+
/** @internal */ _emitPause(event) {
|
|
222
|
+
this.dispatchEvent('pause', event);
|
|
223
|
+
}
|
|
224
|
+
/** @internal */ _emitResume(event) {
|
|
225
|
+
this.dispatchEvent('resume', event);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
10
228
|
function cloneInterpolator(src) {
|
|
11
229
|
return new Interpolator(src.mode, src.target, src.inputs instanceof Float32Array ? new Float32Array(src.inputs) : [
|
|
12
230
|
...src.inputs
|
|
@@ -407,7 +625,7 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
407
625
|
* - Disposing the set releases references to the model, clips, and clears active state.
|
|
408
626
|
*
|
|
409
627
|
* @public
|
|
410
|
-
*/ class AnimationSet extends Disposable {
|
|
628
|
+
*/ class AnimationSet extends makeObservable(Disposable)() {
|
|
411
629
|
/** @internal */ _model;
|
|
412
630
|
/** @internal */ _animations;
|
|
413
631
|
/** @internal */ _skeletons;
|
|
@@ -417,6 +635,14 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
417
635
|
/** @internal */ _activeRigs;
|
|
418
636
|
/** @internal */ _activeAnimations;
|
|
419
637
|
/**
|
|
638
|
+
* Callbacks driven by the same logical clock as the animations (see {@link update}).
|
|
639
|
+
*
|
|
640
|
+
* Used by higher-level constructs (e.g. timeline runners) that need to advance
|
|
641
|
+
* wall-clock-independent timers in sync with playback, so they pause/scale with the
|
|
642
|
+
* game clock instead of running on real time.
|
|
643
|
+
* @internal
|
|
644
|
+
*/ _timelineTickers;
|
|
645
|
+
/**
|
|
420
646
|
* Create an AnimationSet controlling the provided model.
|
|
421
647
|
*
|
|
422
648
|
* @param model - The SceneNode (model root) controlled by this animation set.
|
|
@@ -430,6 +656,19 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
430
656
|
this._activeAnimations = new Map();
|
|
431
657
|
this._skeletons = [];
|
|
432
658
|
this._rigs = [];
|
|
659
|
+
this._timelineTickers = new Set();
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Register a callback advanced by {@link update} using the same delta time as the animations.
|
|
663
|
+
* @internal
|
|
664
|
+
*/ _registerTimelineTicker(ticker) {
|
|
665
|
+
this._timelineTickers.add(ticker);
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Unregister a callback previously registered with {@link _registerTimelineTicker}.
|
|
669
|
+
* @internal
|
|
670
|
+
*/ _unregisterTimelineTicker(ticker) {
|
|
671
|
+
this._timelineTickers.delete(ticker);
|
|
433
672
|
}
|
|
434
673
|
/**
|
|
435
674
|
* The model (SceneNode) controlled by this animation set.
|
|
@@ -533,30 +772,99 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
533
772
|
* @param deltaInSeconds - Time step in seconds since last update.
|
|
534
773
|
*/ update(deltaInSeconds) {
|
|
535
774
|
this._activeAnimations.forEach((v, k)=>{
|
|
775
|
+
if (!v.started) {
|
|
776
|
+
v.started = true;
|
|
777
|
+
const event = this.createPlaybackEvent(v, k);
|
|
778
|
+
v.playback._emitStart(event);
|
|
779
|
+
this.dispatchEvent('playbackstart', event);
|
|
780
|
+
}
|
|
781
|
+
if (v.paused) {
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
536
784
|
if (v.fadeOut > 0 && v.fadeOutStart < 0) {
|
|
537
785
|
v.fadeOutStart = v.animateTime;
|
|
538
786
|
}
|
|
787
|
+
if (v.completed) {
|
|
788
|
+
v.animateTime += Math.abs(deltaInSeconds * v.speedRatio);
|
|
789
|
+
if (v.fadeOut > 0 && v.animateTime - v.fadeOutStart >= v.fadeOut) {
|
|
790
|
+
this.stopAnimation(k.name, {
|
|
791
|
+
reason: v.stopReason
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
539
796
|
// Update animation time
|
|
540
797
|
if (v.firstFrame) {
|
|
541
798
|
v.firstFrame = false;
|
|
542
799
|
} else {
|
|
800
|
+
const previousTime = v.currentTime;
|
|
543
801
|
const timeAdvance = deltaInSeconds * v.speedRatio;
|
|
544
802
|
v.currentTime += timeAdvance;
|
|
545
803
|
v.animateTime += timeAdvance;
|
|
804
|
+
const direction = timeAdvance >= 0 ? 1 : -1;
|
|
546
805
|
if (k.timeDuration > 0) {
|
|
547
|
-
if (v.currentTime
|
|
548
|
-
v.
|
|
806
|
+
if (v.rangeEnd !== null && v.speedRatio >= 0 && v.currentTime >= v.rangeEnd) {
|
|
807
|
+
v.currentTime = v.rangeEnd;
|
|
808
|
+
v.playback._setTime(v.currentTime);
|
|
809
|
+
this.completePlayback(k, v);
|
|
810
|
+
return;
|
|
811
|
+
} else if (v.rangeStart !== null && v.speedRatio < 0 && v.currentTime <= v.rangeStart) {
|
|
812
|
+
v.currentTime = v.rangeStart;
|
|
813
|
+
v.playback._setTime(v.currentTime);
|
|
814
|
+
this.completePlayback(k, v);
|
|
815
|
+
return;
|
|
816
|
+
} else if (v.currentTime > k.timeDuration) {
|
|
817
|
+
const loops = Math.max(1, Math.floor(v.currentTime / k.timeDuration));
|
|
818
|
+
if (v.repeat !== 0 && v.repeatCounter + loops >= v.repeat) {
|
|
819
|
+
v.repeatCounter += loops;
|
|
820
|
+
v.currentTime = k.timeDuration;
|
|
821
|
+
v.playback._setTime(v.currentTime);
|
|
822
|
+
this.emitCrossedMarkers(v, k, previousTime, v.currentTime, direction);
|
|
823
|
+
this.emitCrossedFrames(v, k, previousTime, v.currentTime, direction);
|
|
824
|
+
this.completePlayback(k, v);
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
v.repeatCounter += loops;
|
|
549
828
|
v.currentTime %= k.timeDuration;
|
|
829
|
+
const event = this.createPlaybackEvent(v, k);
|
|
830
|
+
v.playback._emitLoop(event);
|
|
831
|
+
this.dispatchEvent('playbackloop', event);
|
|
550
832
|
} else if (v.currentTime < 0) {
|
|
551
|
-
|
|
833
|
+
const loops = Math.max(1, Math.ceil(-v.currentTime / k.timeDuration));
|
|
834
|
+
if (v.repeat !== 0 && v.repeatCounter + loops >= v.repeat) {
|
|
835
|
+
v.repeatCounter += loops;
|
|
836
|
+
v.currentTime = 0;
|
|
837
|
+
v.playback._setTime(v.currentTime);
|
|
838
|
+
this.emitCrossedMarkers(v, k, previousTime, v.currentTime, direction);
|
|
839
|
+
this.emitCrossedFrames(v, k, previousTime, v.currentTime, direction);
|
|
840
|
+
this.completePlayback(k, v);
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
843
|
+
v.repeatCounter += loops;
|
|
552
844
|
v.currentTime = (v.currentTime % k.timeDuration + k.timeDuration) % k.timeDuration;
|
|
845
|
+
const event = this.createPlaybackEvent(v, k);
|
|
846
|
+
v.playback._emitLoop(event);
|
|
847
|
+
this.dispatchEvent('playbackloop', event);
|
|
553
848
|
}
|
|
554
849
|
}
|
|
850
|
+
v.playback._setTime(v.currentTime);
|
|
851
|
+
this.emitCrossedMarkers(v, k, previousTime, v.currentTime, direction);
|
|
852
|
+
this.emitCrossedFrames(v, k, previousTime, v.currentTime, direction);
|
|
555
853
|
if (v.repeat !== 0 && v.repeatCounter >= v.repeat) {
|
|
556
|
-
this.
|
|
854
|
+
this.completePlayback(k, v);
|
|
557
855
|
} else if (v.fadeOut > 0) {
|
|
558
856
|
if (v.animateTime - v.fadeOutStart >= v.fadeOut) {
|
|
559
|
-
this.stopAnimation(k.name
|
|
857
|
+
this.stopAnimation(k.name, {
|
|
858
|
+
reason: v.stopReason
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
if (v.fadeToDuration > 0) {
|
|
863
|
+
const t = Math.min(1, (v.animateTime - v.fadeToStart) / v.fadeToDuration);
|
|
864
|
+
v.weight = v.fadeFromWeight + (v.fadeTargetWeight - v.fadeFromWeight) * t;
|
|
865
|
+
v.playback._setWeight(v.weight);
|
|
866
|
+
if (t >= 1) {
|
|
867
|
+
v.fadeToDuration = 0;
|
|
560
868
|
}
|
|
561
869
|
}
|
|
562
870
|
}
|
|
@@ -596,6 +904,11 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
596
904
|
this._skeletons.forEach((v)=>{
|
|
597
905
|
v.get()?.apply();
|
|
598
906
|
});
|
|
907
|
+
// Advance logical-clock timers (e.g. timeline `wait` steps) with the same delta time,
|
|
908
|
+
// so they stay in sync with playback and pause/scale with the game clock.
|
|
909
|
+
if (this._timelineTickers.size > 0) {
|
|
910
|
+
this._timelineTickers.forEach((ticker)=>ticker(deltaInSeconds));
|
|
911
|
+
}
|
|
599
912
|
}
|
|
600
913
|
/**
|
|
601
914
|
* Check whether an animation is currently playing.
|
|
@@ -635,80 +948,378 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
635
948
|
const info = this._activeAnimations.get(ani);
|
|
636
949
|
if (info) {
|
|
637
950
|
info.weight = weight;
|
|
951
|
+
info.targetWeight = weight;
|
|
952
|
+
info.playback._setWeight(weight);
|
|
638
953
|
}
|
|
639
954
|
}
|
|
640
955
|
/**
|
|
956
|
+
* Create a playback handle without starting it.
|
|
957
|
+
*/ createPlayback(name, options) {
|
|
958
|
+
const ani = this._animations[name];
|
|
959
|
+
if (!ani) {
|
|
960
|
+
console.error(`Animation ${name} not exists`);
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
return new AnimationPlayback(this, ani, options);
|
|
964
|
+
}
|
|
965
|
+
/**
|
|
966
|
+
* Start an animation and return its playback handle.
|
|
967
|
+
*/ play(name, options) {
|
|
968
|
+
const playback = this.createPlayback(name, options);
|
|
969
|
+
playback?.play();
|
|
970
|
+
return playback;
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Get currently active playbacks.
|
|
974
|
+
*/ getPlaybacks(name) {
|
|
975
|
+
const playbacks = [];
|
|
976
|
+
this._activeAnimations.forEach((info, clip)=>{
|
|
977
|
+
if (!name || clip.name === name) {
|
|
978
|
+
playbacks.push(info.playback);
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
return playbacks;
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Get the currently active playback for a clip.
|
|
985
|
+
*/ getPlayback(name) {
|
|
986
|
+
const clip = this._animations[name];
|
|
987
|
+
return clip ? this._activeAnimations.get(clip)?.playback ?? null : null;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
641
990
|
* Start (or update) playback of an animation clip.
|
|
642
991
|
*
|
|
643
992
|
* Behavior:
|
|
644
|
-
* - If the clip is already playing,
|
|
993
|
+
* - If the clip is already playing, stops it first.
|
|
645
994
|
* - Otherwise initializes playback state (repeat counter, speed, weight, initial time).
|
|
646
995
|
* - Registers clip tracks and skeletons into the active sets for blending and application.
|
|
647
996
|
*
|
|
648
997
|
* @param name - Name of the animation to play.
|
|
649
998
|
* @param options - Playback options (repeat, speedRatio, fadeIn).
|
|
650
999
|
*/ playAnimation(name, options) {
|
|
651
|
-
|
|
1000
|
+
this.play(name, options);
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Start a previously created playback.
|
|
1004
|
+
* @internal
|
|
1005
|
+
*/ startPlayback(playback) {
|
|
1006
|
+
const ani = playback.clip;
|
|
652
1007
|
if (!ani) {
|
|
653
|
-
console.error(`Animation ${name} not exists`);
|
|
654
1008
|
return;
|
|
655
1009
|
}
|
|
1010
|
+
const options = playback._getOptions();
|
|
656
1011
|
const fadeIn = Math.max(options?.fadeIn ?? 0, 0);
|
|
657
|
-
const
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
this.
|
|
666
|
-
|
|
667
|
-
weight,
|
|
668
|
-
speedRatio,
|
|
669
|
-
fadeIn,
|
|
670
|
-
fadeOut: 0,
|
|
671
|
-
repeatCounter: 0,
|
|
672
|
-
currentTime: speedRatio < 0 ? ani.timeDuration : 0,
|
|
673
|
-
animateTime: 0,
|
|
674
|
-
fadeOutStart: 0,
|
|
675
|
-
firstFrame: true
|
|
676
|
-
});
|
|
677
|
-
ani.tracks?.forEach((v, k)=>{
|
|
678
|
-
let nodeTracks = this._activeTracks.get(k);
|
|
679
|
-
if (!nodeTracks) {
|
|
680
|
-
nodeTracks = new Map();
|
|
681
|
-
this._activeTracks.set(k, nodeTracks);
|
|
682
|
-
}
|
|
683
|
-
for (const track of v){
|
|
684
|
-
const blendId = track.getBlendId();
|
|
685
|
-
let blendedTracks = nodeTracks.get(blendId);
|
|
686
|
-
if (!blendedTracks) {
|
|
687
|
-
blendedTracks = [];
|
|
688
|
-
nodeTracks.set(blendId, blendedTracks);
|
|
689
|
-
}
|
|
690
|
-
blendedTracks.push(track);
|
|
691
|
-
}
|
|
1012
|
+
const repeat = options?.repeat ?? 0;
|
|
1013
|
+
const speedRatio = playback.speedRatio;
|
|
1014
|
+
const weight = playback.weight;
|
|
1015
|
+
const rangeStart = ani.resolveTimeRef(options?.range?.start);
|
|
1016
|
+
const rangeEnd = ani.resolveTimeRef(options?.range?.end);
|
|
1017
|
+
const syncSource = this.resolvePlaybackSyncSource(options?.sync);
|
|
1018
|
+
const initialTime = this.computePlaybackInitialTime(ani, speedRatio, rangeStart, rangeEnd, options?.sync, syncSource);
|
|
1019
|
+
if (this.isPlayingAnimation(ani.name)) {
|
|
1020
|
+
this.stopAnimation(ani.name, {
|
|
1021
|
+
reason: 'replaced'
|
|
692
1022
|
});
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1023
|
+
}
|
|
1024
|
+
playback._setState('playing');
|
|
1025
|
+
playback._setTime(initialTime);
|
|
1026
|
+
this._activeAnimations.set(ani, {
|
|
1027
|
+
playback,
|
|
1028
|
+
repeat,
|
|
1029
|
+
weight,
|
|
1030
|
+
targetWeight: weight,
|
|
1031
|
+
speedRatio,
|
|
1032
|
+
fadeIn,
|
|
1033
|
+
fadeOut: 0,
|
|
1034
|
+
repeatCounter: 0,
|
|
1035
|
+
currentTime: initialTime,
|
|
1036
|
+
animateTime: 0,
|
|
1037
|
+
fadeOutStart: 0,
|
|
1038
|
+
fadeToStart: 0,
|
|
1039
|
+
fadeToDuration: 0,
|
|
1040
|
+
fadeFromWeight: weight,
|
|
1041
|
+
fadeTargetWeight: weight,
|
|
1042
|
+
firstFrame: true,
|
|
1043
|
+
started: false,
|
|
1044
|
+
paused: false,
|
|
1045
|
+
stopReason: 'manual',
|
|
1046
|
+
completed: false,
|
|
1047
|
+
rangeStart,
|
|
1048
|
+
rangeEnd
|
|
1049
|
+
});
|
|
1050
|
+
ani.tracks?.forEach((v, k)=>{
|
|
1051
|
+
let nodeTracks = this._activeTracks.get(k);
|
|
1052
|
+
if (!nodeTracks) {
|
|
1053
|
+
nodeTracks = new Map();
|
|
1054
|
+
this._activeTracks.set(k, nodeTracks);
|
|
1055
|
+
}
|
|
1056
|
+
for (const track of v){
|
|
1057
|
+
const blendId = track.getBlendId();
|
|
1058
|
+
let blendedTracks = nodeTracks.get(blendId);
|
|
1059
|
+
if (!blendedTracks) {
|
|
1060
|
+
blendedTracks = [];
|
|
1061
|
+
nodeTracks.set(blendId, blendedTracks);
|
|
709
1062
|
}
|
|
710
|
-
|
|
1063
|
+
blendedTracks.push(track);
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
ani.skeletons?.forEach((v, k)=>{
|
|
1067
|
+
const rig = this.model.findSkeletonRigById(k);
|
|
1068
|
+
if (rig) {
|
|
1069
|
+
const refcount = this._activeRigs.get(rig);
|
|
1070
|
+
this._activeRigs.set(rig, refcount ? refcount + 1 : 1);
|
|
1071
|
+
rig.playing = true;
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
const binding = this.model.findSkinBindingById(k);
|
|
1075
|
+
if (binding) {
|
|
1076
|
+
const refcount = this._activeSkinBindings.get(binding);
|
|
1077
|
+
this._activeSkinBindings.set(binding, refcount ? refcount + 1 : 1);
|
|
1078
|
+
binding.playing = true;
|
|
1079
|
+
const rigRefcount = this._activeRigs.get(binding.rig);
|
|
1080
|
+
this._activeRigs.set(binding.rig, rigRefcount ? rigRefcount + 1 : 1);
|
|
1081
|
+
binding.rig.playing = true;
|
|
1082
|
+
}
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
resolvePlaybackSyncSource(sync) {
|
|
1086
|
+
if (!sync?.target) {
|
|
1087
|
+
return null;
|
|
1088
|
+
}
|
|
1089
|
+
const playback = this.resolveActivePlayback(sync.target);
|
|
1090
|
+
if (!playback) {
|
|
1091
|
+
return null;
|
|
1092
|
+
}
|
|
1093
|
+
const info = this._activeAnimations.get(playback.clip);
|
|
1094
|
+
return info?.playback === playback ? {
|
|
1095
|
+
playback,
|
|
1096
|
+
info,
|
|
1097
|
+
clip: playback.clip
|
|
1098
|
+
} : null;
|
|
1099
|
+
}
|
|
1100
|
+
resolveActivePlayback(target) {
|
|
1101
|
+
const named = this.getPlayback(target);
|
|
1102
|
+
if (named) {
|
|
1103
|
+
return named;
|
|
1104
|
+
}
|
|
1105
|
+
for (const info of this._activeAnimations.values()){
|
|
1106
|
+
if (info.playback.id === target) {
|
|
1107
|
+
return info.playback;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
return null;
|
|
1111
|
+
}
|
|
1112
|
+
computePlaybackInitialTime(clip, speedRatio, rangeStart, rangeEnd, sync, syncSource) {
|
|
1113
|
+
const fallback = speedRatio < 0 ? rangeEnd ?? rangeStart ?? clip.timeDuration : rangeStart ?? 0;
|
|
1114
|
+
if (!sync || !syncSource) {
|
|
1115
|
+
return fallback;
|
|
1116
|
+
}
|
|
1117
|
+
const destination = this.getPlaybackEffectiveRange(clip, rangeStart, rangeEnd);
|
|
1118
|
+
if (destination.duration <= 0) {
|
|
1119
|
+
return fallback;
|
|
1120
|
+
}
|
|
1121
|
+
const offset = Number.isFinite(sync.offset) ? sync.offset ?? 0 : 0;
|
|
1122
|
+
const wrap = sync.wrap ?? true;
|
|
1123
|
+
if ((sync.mode ?? 'normalized') === 'time') {
|
|
1124
|
+
return this.constrainPlaybackTime(syncSource.info.currentTime + offset, destination, wrap);
|
|
711
1125
|
}
|
|
1126
|
+
const source = this.getPlaybackEffectiveRange(syncSource.clip, syncSource.info.rangeStart, syncSource.info.rangeEnd);
|
|
1127
|
+
if (source.duration <= 0) {
|
|
1128
|
+
return fallback;
|
|
1129
|
+
}
|
|
1130
|
+
const phase = (syncSource.info.currentTime - source.start) / source.duration + offset;
|
|
1131
|
+
return this.constrainPlaybackTime(destination.start + phase * destination.duration, destination, wrap);
|
|
1132
|
+
}
|
|
1133
|
+
getPlaybackEffectiveRange(clip, rangeStart, rangeEnd) {
|
|
1134
|
+
const start = rangeStart ?? 0;
|
|
1135
|
+
const end = rangeEnd ?? clip.timeDuration;
|
|
1136
|
+
return start <= end ? {
|
|
1137
|
+
start,
|
|
1138
|
+
end,
|
|
1139
|
+
duration: end - start
|
|
1140
|
+
} : {
|
|
1141
|
+
start: end,
|
|
1142
|
+
end: start,
|
|
1143
|
+
duration: start - end
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
constrainPlaybackTime(time, range, wrap) {
|
|
1147
|
+
if (!Number.isFinite(time)) {
|
|
1148
|
+
return range.start;
|
|
1149
|
+
}
|
|
1150
|
+
if (!wrap) {
|
|
1151
|
+
return Math.max(range.start, Math.min(range.end, time));
|
|
1152
|
+
}
|
|
1153
|
+
if (time >= range.start && time < range.end) {
|
|
1154
|
+
return time;
|
|
1155
|
+
}
|
|
1156
|
+
const offset = ((time - range.start) % range.duration + range.duration) % range.duration;
|
|
1157
|
+
return range.start + offset;
|
|
1158
|
+
}
|
|
1159
|
+
/**
|
|
1160
|
+
* Stop a playback handle.
|
|
1161
|
+
* @internal
|
|
1162
|
+
*/ stopPlayback(playback, options) {
|
|
1163
|
+
if (this._activeAnimations.get(playback.clip)?.playback === playback) {
|
|
1164
|
+
this.stopAnimation(playback.clip.name, options);
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
/**
|
|
1168
|
+
* Pause a playback handle.
|
|
1169
|
+
* @internal
|
|
1170
|
+
*/ pausePlayback(playback) {
|
|
1171
|
+
const info = this._activeAnimations.get(playback.clip);
|
|
1172
|
+
if (info?.playback === playback && !info.paused) {
|
|
1173
|
+
info.paused = true;
|
|
1174
|
+
playback._setState('paused');
|
|
1175
|
+
const event = this.createPlaybackEvent(info, playback.clip);
|
|
1176
|
+
playback._emitPause(event);
|
|
1177
|
+
this.dispatchEvent('playbackpause', event);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Resume a playback handle.
|
|
1182
|
+
* @internal
|
|
1183
|
+
*/ resumePlayback(playback) {
|
|
1184
|
+
const info = this._activeAnimations.get(playback.clip);
|
|
1185
|
+
if (info?.playback === playback && info.paused) {
|
|
1186
|
+
info.paused = false;
|
|
1187
|
+
playback._setState('playing');
|
|
1188
|
+
const event = this.createPlaybackEvent(info, playback.clip);
|
|
1189
|
+
playback._emitResume(event);
|
|
1190
|
+
this.dispatchEvent('playbackresume', event);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
/**
|
|
1194
|
+
* Seek a playback handle.
|
|
1195
|
+
* @internal
|
|
1196
|
+
*/ seekPlayback(playback, time, options) {
|
|
1197
|
+
const clip = playback.clip;
|
|
1198
|
+
const info = this._activeAnimations.get(clip);
|
|
1199
|
+
const duration = clip.timeDuration;
|
|
1200
|
+
const clampedTime = duration > 0 ? Math.max(0, Math.min(duration, time)) : Math.max(0, time);
|
|
1201
|
+
const previousTime = info?.currentTime ?? playback.time;
|
|
1202
|
+
playback._setTime(clampedTime);
|
|
1203
|
+
if (info?.playback === playback) {
|
|
1204
|
+
info.currentTime = clampedTime;
|
|
1205
|
+
if (options?.emitEvents) {
|
|
1206
|
+
const direction = clampedTime >= previousTime ? 1 : -1;
|
|
1207
|
+
this.emitCrossedMarkers(info, clip, previousTime, clampedTime, direction);
|
|
1208
|
+
this.emitCrossedFrames(info, clip, previousTime, clampedTime, direction);
|
|
1209
|
+
}
|
|
1210
|
+
if (options?.apply) {
|
|
1211
|
+
this.update(0);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Set playback weight.
|
|
1217
|
+
* @internal
|
|
1218
|
+
*/ setPlaybackWeight(playback, weight) {
|
|
1219
|
+
playback._setWeight(weight);
|
|
1220
|
+
const info = this._activeAnimations.get(playback.clip);
|
|
1221
|
+
if (info?.playback === playback) {
|
|
1222
|
+
info.weight = weight;
|
|
1223
|
+
info.targetWeight = weight;
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
/**
|
|
1227
|
+
* Set playback speed ratio.
|
|
1228
|
+
* @internal
|
|
1229
|
+
*/ setPlaybackSpeedRatio(playback, speedRatio) {
|
|
1230
|
+
playback._setSpeedRatio(speedRatio);
|
|
1231
|
+
const info = this._activeAnimations.get(playback.clip);
|
|
1232
|
+
if (info?.playback === playback) {
|
|
1233
|
+
info.speedRatio = speedRatio;
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Fade playback weight to a target value.
|
|
1238
|
+
* @internal
|
|
1239
|
+
*/ fadePlaybackTo(playback, weight, duration) {
|
|
1240
|
+
const info = this._activeAnimations.get(playback.clip);
|
|
1241
|
+
if (info?.playback === playback) {
|
|
1242
|
+
info.fadeFromWeight = info.weight;
|
|
1243
|
+
info.fadeTargetWeight = weight;
|
|
1244
|
+
info.targetWeight = weight;
|
|
1245
|
+
info.fadeToStart = info.animateTime;
|
|
1246
|
+
info.fadeToDuration = Math.max(duration, 0);
|
|
1247
|
+
if (info.fadeToDuration === 0) {
|
|
1248
|
+
info.weight = weight;
|
|
1249
|
+
playback._setWeight(weight);
|
|
1250
|
+
}
|
|
1251
|
+
} else {
|
|
1252
|
+
playback._setWeight(weight);
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
createPlaybackEvent(info, clip) {
|
|
1256
|
+
return {
|
|
1257
|
+
playback: info.playback,
|
|
1258
|
+
animationSet: this,
|
|
1259
|
+
clip,
|
|
1260
|
+
time: info.currentTime,
|
|
1261
|
+
normalizedTime: clip.timeDuration > 0 ? info.currentTime / clip.timeDuration : 0
|
|
1262
|
+
};
|
|
1263
|
+
}
|
|
1264
|
+
completePlayback(clip, info) {
|
|
1265
|
+
if (info.completed) {
|
|
1266
|
+
return;
|
|
1267
|
+
}
|
|
1268
|
+
const event = this.createPlaybackEvent(info, clip);
|
|
1269
|
+
const completionFadeOut = Math.max(info.playback._getOptions().completionFadeOut ?? 0, 0);
|
|
1270
|
+
info.playback._setState('completed');
|
|
1271
|
+
info.playback._emitComplete(event);
|
|
1272
|
+
this.dispatchEvent('playbackcomplete', event);
|
|
1273
|
+
if (completionFadeOut > 0) {
|
|
1274
|
+
info.completed = true;
|
|
1275
|
+
info.fadeOut = completionFadeOut;
|
|
1276
|
+
info.fadeOutStart = info.animateTime;
|
|
1277
|
+
info.stopReason = 'completed';
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
this.stopAnimation(clip.name, {
|
|
1281
|
+
reason: 'completed'
|
|
1282
|
+
});
|
|
1283
|
+
}
|
|
1284
|
+
emitCrossedMarkers(info, clip, fromTime, toTime, direction) {
|
|
1285
|
+
for (const marker of this.getCrossedMarkers(clip, fromTime, toTime, direction)){
|
|
1286
|
+
const event = {
|
|
1287
|
+
...this.createPlaybackEvent(info, clip),
|
|
1288
|
+
marker,
|
|
1289
|
+
direction
|
|
1290
|
+
};
|
|
1291
|
+
info.playback._emitMarker(event);
|
|
1292
|
+
this.dispatchEvent('marker', event);
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
emitCrossedFrames(info, clip, fromTime, toTime, direction) {
|
|
1296
|
+
for (const frame of info.playback._getWatchedFrames()){
|
|
1297
|
+
const frameTime = frame / clip.frameRate;
|
|
1298
|
+
const crossed = direction >= 0 ? this.crossedForward(fromTime, toTime, frameTime, clip.timeDuration) : this.crossedBackward(fromTime, toTime, frameTime, clip.timeDuration);
|
|
1299
|
+
if (crossed) {
|
|
1300
|
+
const event = {
|
|
1301
|
+
...this.createPlaybackEvent(info, clip),
|
|
1302
|
+
frame
|
|
1303
|
+
};
|
|
1304
|
+
info.playback._emitFrame(event);
|
|
1305
|
+
this.dispatchEvent('frame', event);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
getCrossedMarkers(clip, fromTime, toTime, direction) {
|
|
1310
|
+
return clip.markers.filter((marker)=>{
|
|
1311
|
+
const markerTime = clip.resolveMarkerTime(marker);
|
|
1312
|
+
if (markerTime === null) {
|
|
1313
|
+
return false;
|
|
1314
|
+
}
|
|
1315
|
+
return direction >= 0 ? this.crossedForward(fromTime, toTime, markerTime, clip.timeDuration) : this.crossedBackward(fromTime, toTime, markerTime, clip.timeDuration);
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
crossedForward(fromTime, toTime, targetTime, duration) {
|
|
1319
|
+
return toTime >= fromTime ? targetTime > fromTime && targetTime <= toTime : duration > 0 && targetTime > fromTime && targetTime <= duration || targetTime <= toTime;
|
|
1320
|
+
}
|
|
1321
|
+
crossedBackward(fromTime, toTime, targetTime, duration) {
|
|
1322
|
+
return toTime <= fromTime ? targetTime < fromTime && targetTime >= toTime : duration > 0 && targetTime < fromTime && targetTime >= 0 || targetTime >= toTime;
|
|
712
1323
|
}
|
|
713
1324
|
/**
|
|
714
1325
|
* Stop playback of an animation clip.
|
|
@@ -731,9 +1342,12 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
731
1342
|
const info = this._activeAnimations.get(ani);
|
|
732
1343
|
if (info) {
|
|
733
1344
|
const fadeOut = Math.max(options?.fadeOut ?? 0, 0);
|
|
1345
|
+
const reason = options?.reason ?? 'manual';
|
|
734
1346
|
if (fadeOut !== 0) {
|
|
735
1347
|
info.fadeOut = fadeOut;
|
|
736
1348
|
info.fadeOutStart = -1;
|
|
1349
|
+
info.stopReason = reason;
|
|
1350
|
+
info.playback._setState('stopping');
|
|
737
1351
|
} else {
|
|
738
1352
|
this._activeAnimations.delete(ani);
|
|
739
1353
|
this._activeTracks.forEach((v)=>{
|
|
@@ -771,6 +1385,15 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
771
1385
|
}
|
|
772
1386
|
}
|
|
773
1387
|
});
|
|
1388
|
+
const event = {
|
|
1389
|
+
...this.createPlaybackEvent(info, ani),
|
|
1390
|
+
reason
|
|
1391
|
+
};
|
|
1392
|
+
if (reason !== 'completed') {
|
|
1393
|
+
info.playback._setState('stopped');
|
|
1394
|
+
}
|
|
1395
|
+
info.playback._emitStop(event);
|
|
1396
|
+
this.dispatchEvent('playbackstop', event);
|
|
774
1397
|
}
|
|
775
1398
|
}
|
|
776
1399
|
}
|
|
@@ -1179,5 +1802,5 @@ function bakeHumanoidRotationTracks(sourceClip, srcSkeleton, dstSkeleton, srcRoo
|
|
|
1179
1802
|
}
|
|
1180
1803
|
}
|
|
1181
1804
|
|
|
1182
|
-
export { AnimationSet };
|
|
1805
|
+
export { AnimationPlayback, AnimationSet };
|
|
1183
1806
|
//# sourceMappingURL=animationset.js.map
|