@lovelace_lol/loom3 1.0.35 → 1.0.37
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/README.md +16 -1
- package/dist/index.cjs +732 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -1
- package/dist/index.d.ts +52 -1
- package/dist/index.js +732 -93
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
|
-
import { Vector3, Clock, Box3, Quaternion, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopPingPong, LoopOnce, LoopRepeat, QuaternionKeyframeTrack, NumberKeyframeTrack, AnimationClip, AnimationMixer, Mesh } from 'three';
|
|
2
|
+
import { Vector3, Clock, Box3, Quaternion, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopPingPong, LoopOnce, LoopRepeat, QuaternionKeyframeTrack, NumberKeyframeTrack, AnimationClip, AnimationMixer, Mesh, PropertyBinding } from 'three';
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -58,6 +58,119 @@ function resolveCurveBalance(curveId, globalBalance, balanceMap) {
|
|
|
58
58
|
}
|
|
59
59
|
return clampBalance(globalBalance);
|
|
60
60
|
}
|
|
61
|
+
var RUNTIME_CLIP_PREFIX = "__loom3_baked_partition__/";
|
|
62
|
+
var FACE_SAFE_TARGET_RE = /(head|neck|jaw|eye|brow|lid|mouth|lip|face|cheek|nose|tongue|teeth)/i;
|
|
63
|
+
var BODY_LIKE_TARGET_RE = /(root|armature|hips?|pelvis|spine|waist|chest|torso|shoulder|arm|forearm|hand|finger|leg|thigh|calf|knee|foot|toe|tail|wing|fin|body|abdomen|clavicle)/i;
|
|
64
|
+
var SCENE_LIKE_TARGET_RE = /(camera|cam|scene|world|global|origin|pivot|cube)/i;
|
|
65
|
+
var CHANNEL_ORDER = ["face", "body", "scene"];
|
|
66
|
+
function getRuntimeClipName(sourceClipName, channel) {
|
|
67
|
+
return `${RUNTIME_CLIP_PREFIX}${sourceClipName}/${channel}`;
|
|
68
|
+
}
|
|
69
|
+
function parseTrackTarget(trackName, model) {
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = PropertyBinding.parseTrackName(trackName);
|
|
73
|
+
} catch {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const targetKey = parsed.objectName === "bones" && parsed.objectIndex ? String(parsed.objectIndex) : parsed.nodeName;
|
|
77
|
+
const target = targetKey ? model.getObjectByProperty("uuid", targetKey) ?? PropertyBinding.findNode(model, targetKey) : null;
|
|
78
|
+
return {
|
|
79
|
+
propertyName: parsed.propertyName,
|
|
80
|
+
target,
|
|
81
|
+
targetName: target?.name ?? parsed.nodeName ?? ""
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function isSceneTrackTarget(target, targetName) {
|
|
85
|
+
if (!target) return true;
|
|
86
|
+
if (target.isCamera) return true;
|
|
87
|
+
return SCENE_LIKE_TARGET_RE.test(targetName);
|
|
88
|
+
}
|
|
89
|
+
function isFaceSafeTransformTarget(target, targetName, safeTransformTargets) {
|
|
90
|
+
if (target && safeTransformTargets.has(target)) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (!targetName) {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
if (BODY_LIKE_TARGET_RE.test(targetName) || SCENE_LIKE_TARGET_RE.test(targetName)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
return FACE_SAFE_TARGET_RE.test(targetName);
|
|
100
|
+
}
|
|
101
|
+
function classifyBakedTrack(track, model, bones) {
|
|
102
|
+
const parsed = parseTrackTarget(track.name, model);
|
|
103
|
+
if (!parsed) {
|
|
104
|
+
return "scene";
|
|
105
|
+
}
|
|
106
|
+
if (parsed.propertyName === "morphTargetInfluences" || parsed.propertyName === "weights") {
|
|
107
|
+
return "face";
|
|
108
|
+
}
|
|
109
|
+
if (isSceneTrackTarget(parsed.target, parsed.targetName)) {
|
|
110
|
+
return "scene";
|
|
111
|
+
}
|
|
112
|
+
if (parsed.propertyName === "quaternion") {
|
|
113
|
+
const safeTransformTargets = new Set(
|
|
114
|
+
Object.values(bones).map((entry) => entry?.obj).filter((entry) => !!entry)
|
|
115
|
+
);
|
|
116
|
+
if (isFaceSafeTransformTarget(parsed.target, parsed.targetName, safeTransformTargets)) {
|
|
117
|
+
return "face";
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return "body";
|
|
121
|
+
}
|
|
122
|
+
function resolveBakedChannelBlendMode(channel, requestedBlendMode) {
|
|
123
|
+
if (channel === "face") {
|
|
124
|
+
return requestedBlendMode === "additive" ? "additive" : "replace";
|
|
125
|
+
}
|
|
126
|
+
if (channel === "body") {
|
|
127
|
+
return "replace";
|
|
128
|
+
}
|
|
129
|
+
return void 0;
|
|
130
|
+
}
|
|
131
|
+
function resolveBakedAggregateBlendMode(channels, requestedBlendMode) {
|
|
132
|
+
if (requestedBlendMode !== "additive") {
|
|
133
|
+
return "replace";
|
|
134
|
+
}
|
|
135
|
+
return channels.some((channel) => channel.channel === "face" && channel.playable && channel.trackCount > 0) ? "additive" : "replace";
|
|
136
|
+
}
|
|
137
|
+
function partitionBakedClip(clip, model, bones) {
|
|
138
|
+
const tracksByChannel = new Map(
|
|
139
|
+
CHANNEL_ORDER.map((channel) => [channel, []])
|
|
140
|
+
);
|
|
141
|
+
for (const track of clip.tracks) {
|
|
142
|
+
const channel = classifyBakedTrack(track, model, bones);
|
|
143
|
+
tracksByChannel.get(channel)?.push(track.clone());
|
|
144
|
+
}
|
|
145
|
+
const runtimeClips = [];
|
|
146
|
+
const channels = [];
|
|
147
|
+
for (const channel of CHANNEL_ORDER) {
|
|
148
|
+
const tracks = tracksByChannel.get(channel) ?? [];
|
|
149
|
+
if (tracks.length === 0) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const playable = channel !== "scene";
|
|
153
|
+
const blendMode = resolveBakedChannelBlendMode(channel, "additive");
|
|
154
|
+
channels.push({
|
|
155
|
+
channel,
|
|
156
|
+
trackCount: tracks.length,
|
|
157
|
+
playable,
|
|
158
|
+
blendMode
|
|
159
|
+
});
|
|
160
|
+
if (!playable) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
runtimeClips.push({
|
|
164
|
+
channel,
|
|
165
|
+
clip: new AnimationClip(getRuntimeClipName(clip.name, channel), clip.duration, tracks)
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
sourceClip: clip,
|
|
170
|
+
channels,
|
|
171
|
+
runtimeClips
|
|
172
|
+
};
|
|
173
|
+
}
|
|
61
174
|
|
|
62
175
|
// src/engines/three/AnimationThree.ts
|
|
63
176
|
var easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
@@ -157,12 +270,18 @@ var makeActionId = () => `act_${Math.random().toString(36).slice(2, 8)}_${Date.n
|
|
|
157
270
|
var X_AXIS = new Vector3(1, 0, 0);
|
|
158
271
|
var Y_AXIS = new Vector3(0, 1, 0);
|
|
159
272
|
var Z_AXIS = new Vector3(0, 0, 1);
|
|
273
|
+
var CLIP_EVENT_METADATA_KEY = "__loom3ClipEvents";
|
|
274
|
+
var CLIP_EVENT_EPSILON = 1e-4;
|
|
160
275
|
var BakedAnimationController = class {
|
|
161
276
|
constructor(host) {
|
|
162
277
|
__publicField(this, "host");
|
|
163
278
|
__publicField(this, "animationMixer", null);
|
|
164
279
|
__publicField(this, "mixerFinishedListenerAttached", false);
|
|
165
280
|
__publicField(this, "animationClips", []);
|
|
281
|
+
__publicField(this, "bakedSourceClips", /* @__PURE__ */ new Map());
|
|
282
|
+
__publicField(this, "bakedRuntimeActions", /* @__PURE__ */ new Map());
|
|
283
|
+
__publicField(this, "bakedActionGroups", /* @__PURE__ */ new Map());
|
|
284
|
+
__publicField(this, "bakedRuntimeClipToSource", /* @__PURE__ */ new Map());
|
|
166
285
|
__publicField(this, "animationActions", /* @__PURE__ */ new Map());
|
|
167
286
|
__publicField(this, "animationFinishedCallbacks", /* @__PURE__ */ new Map());
|
|
168
287
|
__publicField(this, "clipActions", /* @__PURE__ */ new Map());
|
|
@@ -171,6 +290,7 @@ var BakedAnimationController = class {
|
|
|
171
290
|
__publicField(this, "playbackState", /* @__PURE__ */ new Map());
|
|
172
291
|
__publicField(this, "actionIds", /* @__PURE__ */ new WeakMap());
|
|
173
292
|
__publicField(this, "actionIdToClip", /* @__PURE__ */ new Map());
|
|
293
|
+
__publicField(this, "clipMonitors", /* @__PURE__ */ new Map());
|
|
174
294
|
this.host = host;
|
|
175
295
|
}
|
|
176
296
|
getActionId(action) {
|
|
@@ -184,6 +304,161 @@ var BakedAnimationController = class {
|
|
|
184
304
|
action.__actionId = actionId;
|
|
185
305
|
return actionId;
|
|
186
306
|
}
|
|
307
|
+
setClipEventMetadata(clip, metadata) {
|
|
308
|
+
const userData = clip.userData ?? (clip.userData = {});
|
|
309
|
+
userData[CLIP_EVENT_METADATA_KEY] = metadata;
|
|
310
|
+
}
|
|
311
|
+
getClipEventMetadata(clip) {
|
|
312
|
+
const userData = clip.userData;
|
|
313
|
+
const keyframeTimes = Array.isArray(userData?.[CLIP_EVENT_METADATA_KEY]?.keyframeTimes) ? userData[CLIP_EVENT_METADATA_KEY].keyframeTimes.filter((time) => Number.isFinite(time)) : [];
|
|
314
|
+
return { keyframeTimes };
|
|
315
|
+
}
|
|
316
|
+
getKeyframeIndex(times, currentTime) {
|
|
317
|
+
if (!times.length) return -1;
|
|
318
|
+
const target = Math.max(0, currentTime) + 1e-3;
|
|
319
|
+
let lo = 0;
|
|
320
|
+
let hi = times.length - 1;
|
|
321
|
+
let idx = 0;
|
|
322
|
+
while (lo <= hi) {
|
|
323
|
+
const mid = lo + hi >>> 1;
|
|
324
|
+
if (times[mid] <= target) {
|
|
325
|
+
idx = mid;
|
|
326
|
+
lo = mid + 1;
|
|
327
|
+
} else {
|
|
328
|
+
hi = mid - 1;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return idx;
|
|
332
|
+
}
|
|
333
|
+
emitClipEvent(monitor, event) {
|
|
334
|
+
for (const listener of Array.from(monitor.listeners)) {
|
|
335
|
+
try {
|
|
336
|
+
listener(event);
|
|
337
|
+
} catch (error) {
|
|
338
|
+
console.error("[Loom3] clip event listener failed", error);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
emitKeyframesForRange(monitor, startTime, endTime, direction, includeStart) {
|
|
343
|
+
if (!monitor.keyframeTimes.length) return;
|
|
344
|
+
const times = direction === 1 ? monitor.keyframeTimes : [...monitor.keyframeTimes].reverse();
|
|
345
|
+
for (const time of times) {
|
|
346
|
+
const matchesForward = direction === 1 && (includeStart ? time >= startTime - CLIP_EVENT_EPSILON : time > startTime + CLIP_EVENT_EPSILON) && time <= endTime + CLIP_EVENT_EPSILON;
|
|
347
|
+
const matchesReverse = direction === -1 && (includeStart ? time <= startTime + CLIP_EVENT_EPSILON : time < startTime - CLIP_EVENT_EPSILON) && time >= endTime - CLIP_EVENT_EPSILON;
|
|
348
|
+
if (!matchesForward && !matchesReverse) continue;
|
|
349
|
+
const keyframeIndex = monitor.keyframeTimes.indexOf(time);
|
|
350
|
+
monitor.lastKeyframeIndex = keyframeIndex;
|
|
351
|
+
this.emitClipEvent(monitor, {
|
|
352
|
+
type: "keyframe",
|
|
353
|
+
clipName: monitor.clipName,
|
|
354
|
+
keyframeIndex,
|
|
355
|
+
totalKeyframes: monitor.keyframeTimes.length,
|
|
356
|
+
currentTime: time,
|
|
357
|
+
duration: monitor.duration,
|
|
358
|
+
iteration: monitor.iteration
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
resetClipMonitor(monitor, currentTime) {
|
|
363
|
+
monitor.iteration = 0;
|
|
364
|
+
monitor.direction = monitor.initialDirection;
|
|
365
|
+
monitor.lastTime = currentTime;
|
|
366
|
+
monitor.lastKeyframeIndex = this.getKeyframeIndex(monitor.keyframeTimes, currentTime);
|
|
367
|
+
monitor.finishedPending = false;
|
|
368
|
+
}
|
|
369
|
+
syncClipMonitorTime(monitor, currentTime, emitSeek = false) {
|
|
370
|
+
const clamped = Math.max(0, Math.min(monitor.duration, currentTime));
|
|
371
|
+
monitor.lastTime = clamped;
|
|
372
|
+
monitor.lastKeyframeIndex = this.getKeyframeIndex(monitor.keyframeTimes, clamped);
|
|
373
|
+
if (emitSeek) {
|
|
374
|
+
this.emitClipEvent(monitor, {
|
|
375
|
+
type: "seek",
|
|
376
|
+
clipName: monitor.clipName,
|
|
377
|
+
currentTime: clamped,
|
|
378
|
+
duration: monitor.duration,
|
|
379
|
+
iteration: monitor.iteration
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
cleanupClipMonitor(actionId) {
|
|
384
|
+
const monitor = this.clipMonitors.get(actionId);
|
|
385
|
+
if (!monitor || monitor.cleanedUp) return;
|
|
386
|
+
monitor.cleanedUp = true;
|
|
387
|
+
try {
|
|
388
|
+
monitor.action.paused = true;
|
|
389
|
+
} catch {
|
|
390
|
+
}
|
|
391
|
+
monitor.resolveFinished();
|
|
392
|
+
monitor.listeners.clear();
|
|
393
|
+
this.clipMonitors.delete(actionId);
|
|
394
|
+
this.actionIdToClip.delete(actionId);
|
|
395
|
+
}
|
|
396
|
+
advanceClipMonitor(monitor, previousTime) {
|
|
397
|
+
if (monitor.cleanedUp || monitor.action.paused && !monitor.finishedPending) return;
|
|
398
|
+
const currentTime = Math.max(0, Math.min(monitor.duration, monitor.action.time));
|
|
399
|
+
const delta = currentTime - previousTime;
|
|
400
|
+
if (monitor.loopMode === "pingpong") {
|
|
401
|
+
const movingForward = monitor.direction === 1;
|
|
402
|
+
const bouncedAtEnd = movingForward && delta < -CLIP_EVENT_EPSILON;
|
|
403
|
+
const bouncedAtStart = !movingForward && delta > CLIP_EVENT_EPSILON;
|
|
404
|
+
if (bouncedAtEnd) {
|
|
405
|
+
this.emitKeyframesForRange(monitor, previousTime, monitor.duration, 1, false);
|
|
406
|
+
monitor.direction = -1;
|
|
407
|
+
this.emitKeyframesForRange(monitor, monitor.duration, currentTime, -1, false);
|
|
408
|
+
} else if (bouncedAtStart) {
|
|
409
|
+
this.emitKeyframesForRange(monitor, previousTime, 0, -1, false);
|
|
410
|
+
monitor.direction = 1;
|
|
411
|
+
monitor.iteration += 1;
|
|
412
|
+
this.emitClipEvent(monitor, {
|
|
413
|
+
type: "loop",
|
|
414
|
+
clipName: monitor.clipName,
|
|
415
|
+
iteration: monitor.iteration,
|
|
416
|
+
currentTime: 0,
|
|
417
|
+
duration: monitor.duration
|
|
418
|
+
});
|
|
419
|
+
this.emitKeyframesForRange(monitor, 0, currentTime, 1, false);
|
|
420
|
+
} else if (delta > CLIP_EVENT_EPSILON) {
|
|
421
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, 1, false);
|
|
422
|
+
monitor.direction = 1;
|
|
423
|
+
} else if (delta < -CLIP_EVENT_EPSILON) {
|
|
424
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, -1, false);
|
|
425
|
+
monitor.direction = -1;
|
|
426
|
+
}
|
|
427
|
+
} else if (monitor.direction === 1) {
|
|
428
|
+
const wrapped = currentTime + CLIP_EVENT_EPSILON < previousTime;
|
|
429
|
+
if (wrapped) {
|
|
430
|
+
this.emitKeyframesForRange(monitor, previousTime, monitor.duration, 1, false);
|
|
431
|
+
monitor.iteration += 1;
|
|
432
|
+
this.emitClipEvent(monitor, {
|
|
433
|
+
type: "loop",
|
|
434
|
+
clipName: monitor.clipName,
|
|
435
|
+
iteration: monitor.iteration,
|
|
436
|
+
currentTime: 0,
|
|
437
|
+
duration: monitor.duration
|
|
438
|
+
});
|
|
439
|
+
this.emitKeyframesForRange(monitor, 0, currentTime, 1, true);
|
|
440
|
+
} else if (delta > CLIP_EVENT_EPSILON) {
|
|
441
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, 1, false);
|
|
442
|
+
}
|
|
443
|
+
} else {
|
|
444
|
+
const wrapped = currentTime > previousTime + CLIP_EVENT_EPSILON;
|
|
445
|
+
if (wrapped) {
|
|
446
|
+
this.emitKeyframesForRange(monitor, previousTime, 0, -1, false);
|
|
447
|
+
monitor.iteration += 1;
|
|
448
|
+
this.emitClipEvent(monitor, {
|
|
449
|
+
type: "loop",
|
|
450
|
+
clipName: monitor.clipName,
|
|
451
|
+
iteration: monitor.iteration,
|
|
452
|
+
currentTime: monitor.duration,
|
|
453
|
+
duration: monitor.duration
|
|
454
|
+
});
|
|
455
|
+
this.emitKeyframesForRange(monitor, monitor.duration, currentTime, -1, true);
|
|
456
|
+
} else if (delta < -CLIP_EVENT_EPSILON) {
|
|
457
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, -1, false);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
this.syncClipMonitorTime(monitor, currentTime);
|
|
461
|
+
}
|
|
187
462
|
normalizePlaybackOptions(options, defaults) {
|
|
188
463
|
const clipOptions = options;
|
|
189
464
|
const rawRate = options?.playbackRate ?? options?.speed ?? 1;
|
|
@@ -191,6 +466,7 @@ var BakedAnimationController = class {
|
|
|
191
466
|
const rawWeight = options?.weight ?? options?.intensity ?? clipOptions?.mixerWeight ?? 1;
|
|
192
467
|
const weight = Number.isFinite(rawWeight) ? Math.max(0, rawWeight) : 1;
|
|
193
468
|
const loopMode = options?.loopMode ?? (typeof options?.loop === "boolean" ? options.loop ? "repeat" : "once" : defaults.loop ? "repeat" : "once");
|
|
469
|
+
const requestedBlendMode = options?.blendMode ?? (clipOptions?.mixerAdditive ? "additive" : "replace");
|
|
194
470
|
return {
|
|
195
471
|
source: options?.source ?? defaults.source,
|
|
196
472
|
loop: loopMode !== "once",
|
|
@@ -200,7 +476,8 @@ var BakedAnimationController = class {
|
|
|
200
476
|
playbackRate,
|
|
201
477
|
weight,
|
|
202
478
|
balance: Number.isFinite(options?.balance) ? options?.balance ?? 0 : 0,
|
|
203
|
-
|
|
479
|
+
requestedBlendMode,
|
|
480
|
+
blendMode: requestedBlendMode,
|
|
204
481
|
easing: options?.easing ?? "linear"
|
|
205
482
|
};
|
|
206
483
|
}
|
|
@@ -260,15 +537,49 @@ var BakedAnimationController = class {
|
|
|
260
537
|
next.balance = Math.max(-1, Math.min(1, options.balance));
|
|
261
538
|
}
|
|
262
539
|
if (options.blendMode) {
|
|
263
|
-
next.
|
|
540
|
+
next.requestedBlendMode = options.blendMode;
|
|
264
541
|
} else if (typeof clipOptions?.mixerAdditive === "boolean") {
|
|
265
|
-
next.
|
|
542
|
+
next.requestedBlendMode = clipOptions.mixerAdditive ? "additive" : "replace";
|
|
266
543
|
}
|
|
544
|
+
next.blendMode = next.requestedBlendMode;
|
|
267
545
|
if (options.easing) {
|
|
268
546
|
next.easing = options.easing;
|
|
269
547
|
}
|
|
270
548
|
return next;
|
|
271
549
|
}
|
|
550
|
+
isBakedSourceClip(clipName) {
|
|
551
|
+
return this.bakedSourceClips.has(clipName);
|
|
552
|
+
}
|
|
553
|
+
getBakedSourceClip(clipName) {
|
|
554
|
+
return this.bakedSourceClips.get(clipName);
|
|
555
|
+
}
|
|
556
|
+
getBakedChannelInfo(clipName, playbackState) {
|
|
557
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
558
|
+
if (!bakedClip) {
|
|
559
|
+
return void 0;
|
|
560
|
+
}
|
|
561
|
+
const requestedBlendMode = playbackState?.requestedBlendMode ?? "replace";
|
|
562
|
+
return bakedClip.channels.map((channel) => ({
|
|
563
|
+
...channel,
|
|
564
|
+
blendMode: resolveBakedChannelBlendMode(channel.channel, requestedBlendMode)
|
|
565
|
+
}));
|
|
566
|
+
}
|
|
567
|
+
getBakedAggregateBlendMode(clipName, playbackState) {
|
|
568
|
+
const channels = this.getBakedChannelInfo(clipName, playbackState);
|
|
569
|
+
if (!channels) {
|
|
570
|
+
return playbackState?.requestedBlendMode ?? playbackState?.blendMode ?? "replace";
|
|
571
|
+
}
|
|
572
|
+
return resolveBakedAggregateBlendMode(
|
|
573
|
+
channels,
|
|
574
|
+
playbackState?.requestedBlendMode ?? "replace"
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
applyPlaybackStateToBakedAction(action, state, channel) {
|
|
578
|
+
this.applyPlaybackState(action, {
|
|
579
|
+
...state,
|
|
580
|
+
blendMode: resolveBakedChannelBlendMode(channel, state.requestedBlendMode) ?? "replace"
|
|
581
|
+
});
|
|
582
|
+
}
|
|
272
583
|
resolveStartTime(duration, state, explicitStartTime) {
|
|
273
584
|
if (typeof explicitStartTime === "number" && Number.isFinite(explicitStartTime)) {
|
|
274
585
|
return Math.max(0, Math.min(duration, explicitStartTime));
|
|
@@ -278,8 +589,13 @@ var BakedAnimationController = class {
|
|
|
278
589
|
}
|
|
279
590
|
return 0;
|
|
280
591
|
}
|
|
281
|
-
|
|
282
|
-
const
|
|
592
|
+
getOrCreateBakedRuntimeAction(sourceClipName, channel) {
|
|
593
|
+
const bakedClip = this.getBakedSourceClip(sourceClipName);
|
|
594
|
+
const runtimeClip = bakedClip?.runtimeClips.find((entry) => entry.channel === channel)?.clip;
|
|
595
|
+
if (!runtimeClip) {
|
|
596
|
+
return null;
|
|
597
|
+
}
|
|
598
|
+
const existing = this.bakedRuntimeActions.get(runtimeClip.name);
|
|
283
599
|
if (existing) {
|
|
284
600
|
return existing;
|
|
285
601
|
}
|
|
@@ -287,13 +603,44 @@ var BakedAnimationController = class {
|
|
|
287
603
|
if (!this.animationMixer) {
|
|
288
604
|
return null;
|
|
289
605
|
}
|
|
290
|
-
const
|
|
291
|
-
|
|
606
|
+
const action = this.animationMixer.clipAction(runtimeClip);
|
|
607
|
+
this.bakedRuntimeActions.set(runtimeClip.name, action);
|
|
608
|
+
return action;
|
|
609
|
+
}
|
|
610
|
+
getRepresentativeBakedAction(clipName) {
|
|
611
|
+
const group = this.bakedActionGroups.get(clipName);
|
|
612
|
+
if (!group) {
|
|
292
613
|
return null;
|
|
293
614
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
615
|
+
return group.channelActions.values().next().value ?? null;
|
|
616
|
+
}
|
|
617
|
+
createBakedActionGroup(clipName, playbackState) {
|
|
618
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
619
|
+
if (!bakedClip) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
const channelActions = /* @__PURE__ */ new Map();
|
|
623
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
624
|
+
const action = this.getOrCreateBakedRuntimeAction(clipName, runtimeClip.channel);
|
|
625
|
+
if (action) {
|
|
626
|
+
channelActions.set(runtimeClip.channel, action);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (channelActions.size === 0) {
|
|
630
|
+
return null;
|
|
631
|
+
}
|
|
632
|
+
let resolveFinished = () => {
|
|
633
|
+
};
|
|
634
|
+
const finishedPromise = new Promise((resolve) => {
|
|
635
|
+
resolveFinished = resolve;
|
|
636
|
+
});
|
|
637
|
+
return {
|
|
638
|
+
actionId: makeActionId(),
|
|
639
|
+
channelActions,
|
|
640
|
+
pendingFinishedChannels: playbackState.loopMode === "once" ? new Set(channelActions.keys()) : /* @__PURE__ */ new Set(),
|
|
641
|
+
finishedPromise,
|
|
642
|
+
resolveFinished
|
|
643
|
+
};
|
|
297
644
|
}
|
|
298
645
|
getMeshNamesForAU(auId, config, explicitMeshNames) {
|
|
299
646
|
if (explicitMeshNames && explicitMeshNames.length > 0) {
|
|
@@ -323,7 +670,28 @@ var BakedAnimationController = class {
|
|
|
323
670
|
}
|
|
324
671
|
update(dtSeconds) {
|
|
325
672
|
if (this.animationMixer) {
|
|
673
|
+
const snapshots = Array.from(this.clipMonitors.values()).map((monitor) => ({
|
|
674
|
+
actionId: monitor.actionId,
|
|
675
|
+
previousTime: monitor.action.time
|
|
676
|
+
}));
|
|
326
677
|
this.animationMixer.update(dtSeconds);
|
|
678
|
+
for (const { actionId, previousTime } of snapshots) {
|
|
679
|
+
const monitor = this.clipMonitors.get(actionId);
|
|
680
|
+
if (!monitor) continue;
|
|
681
|
+
this.advanceClipMonitor(monitor, previousTime);
|
|
682
|
+
if (monitor.finishedPending) {
|
|
683
|
+
const finalTime = Math.max(0, Math.min(monitor.duration, monitor.action.time));
|
|
684
|
+
this.syncClipMonitorTime(monitor, finalTime);
|
|
685
|
+
this.emitClipEvent(monitor, {
|
|
686
|
+
type: "completed",
|
|
687
|
+
clipName: monitor.clipName,
|
|
688
|
+
currentTime: finalTime,
|
|
689
|
+
duration: monitor.duration,
|
|
690
|
+
iteration: monitor.iteration
|
|
691
|
+
});
|
|
692
|
+
this.cleanupClipMonitor(actionId);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
327
695
|
}
|
|
328
696
|
}
|
|
329
697
|
dispose() {
|
|
@@ -333,25 +701,60 @@ var BakedAnimationController = class {
|
|
|
333
701
|
this.animationMixer = null;
|
|
334
702
|
}
|
|
335
703
|
this.animationClips = [];
|
|
704
|
+
this.bakedSourceClips.clear();
|
|
705
|
+
this.bakedRuntimeActions.clear();
|
|
706
|
+
this.bakedActionGroups.clear();
|
|
707
|
+
this.bakedRuntimeClipToSource.clear();
|
|
336
708
|
this.animationActions.clear();
|
|
337
709
|
this.animationFinishedCallbacks.clear();
|
|
338
710
|
this.clipActions.clear();
|
|
339
711
|
this.clipHandles.clear();
|
|
340
712
|
this.clipSources.clear();
|
|
341
713
|
this.playbackState.clear();
|
|
714
|
+
this.clipMonitors.clear();
|
|
342
715
|
}
|
|
343
716
|
loadAnimationClips(clips) {
|
|
344
|
-
|
|
717
|
+
const model = this.host.getModel();
|
|
718
|
+
if (!model) {
|
|
345
719
|
console.warn("Loom3: Cannot load animation clips before calling onReady()");
|
|
346
720
|
return;
|
|
347
721
|
}
|
|
722
|
+
for (const clipName of this.bakedSourceClips.keys()) {
|
|
723
|
+
this.stopAnimation(clipName);
|
|
724
|
+
}
|
|
725
|
+
if (this.animationMixer) {
|
|
726
|
+
for (const bakedClip of this.bakedSourceClips.values()) {
|
|
727
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
728
|
+
try {
|
|
729
|
+
this.animationMixer.uncacheAction(runtimeClip.clip);
|
|
730
|
+
} catch {
|
|
731
|
+
}
|
|
732
|
+
try {
|
|
733
|
+
this.animationMixer.uncacheClip(runtimeClip.clip);
|
|
734
|
+
} catch {
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
for (const clipName of this.bakedSourceClips.keys()) {
|
|
740
|
+
this.playbackState.delete(clipName);
|
|
741
|
+
this.clipSources.delete(clipName);
|
|
742
|
+
}
|
|
743
|
+
this.bakedSourceClips.clear();
|
|
744
|
+
this.bakedRuntimeActions.clear();
|
|
745
|
+
this.bakedActionGroups.clear();
|
|
746
|
+
this.bakedRuntimeClipToSource.clear();
|
|
348
747
|
this.ensureMixer();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
748
|
+
const partitionedClips = clips.map((clip) => partitionBakedClip(clip, model, this.host.getBones()));
|
|
749
|
+
this.animationClips = partitionedClips.map((clip) => clip.sourceClip);
|
|
750
|
+
for (const bakedClip of partitionedClips) {
|
|
751
|
+
this.bakedSourceClips.set(bakedClip.sourceClip.name, bakedClip);
|
|
752
|
+
this.clipSources.set(bakedClip.sourceClip.name, "baked");
|
|
753
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
754
|
+
this.bakedRuntimeClipToSource.set(runtimeClip.clip.name, {
|
|
755
|
+
sourceClipName: bakedClip.sourceClip.name,
|
|
756
|
+
channel: runtimeClip.channel
|
|
757
|
+
});
|
|
355
758
|
}
|
|
356
759
|
}
|
|
357
760
|
}
|
|
@@ -360,86 +763,94 @@ var BakedAnimationController = class {
|
|
|
360
763
|
name: clip.name,
|
|
361
764
|
duration: clip.duration,
|
|
362
765
|
trackCount: clip.tracks.length,
|
|
363
|
-
source: this.clipSources.get(clip.name) ?? "baked"
|
|
766
|
+
source: this.clipSources.get(clip.name) ?? "baked",
|
|
767
|
+
channels: this.getBakedSourceClip(clip.name)?.channels
|
|
364
768
|
}));
|
|
365
769
|
}
|
|
366
770
|
removeAnimationClip(clipName) {
|
|
367
|
-
const
|
|
368
|
-
if (!
|
|
771
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
772
|
+
if (!bakedClip) {
|
|
369
773
|
return false;
|
|
370
774
|
}
|
|
371
|
-
const relatedActions = /* @__PURE__ */ new Set();
|
|
372
|
-
const bakedAction = this.animationActions.get(clipName);
|
|
373
|
-
const clipAction = this.clipActions.get(clipName);
|
|
374
|
-
if (bakedAction) relatedActions.add(bakedAction);
|
|
375
|
-
if (clipAction) relatedActions.add(clipAction);
|
|
376
775
|
this.stopAnimation(clipName);
|
|
377
776
|
if (this.animationMixer) {
|
|
378
|
-
for (const
|
|
777
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
778
|
+
const action = this.bakedRuntimeActions.get(runtimeClip.clip.name);
|
|
379
779
|
try {
|
|
380
|
-
this.animationMixer.uncacheAction(clip);
|
|
780
|
+
this.animationMixer.uncacheAction(runtimeClip.clip);
|
|
381
781
|
} catch {
|
|
382
782
|
}
|
|
383
783
|
try {
|
|
384
|
-
this.animationMixer.uncacheClip(clip);
|
|
784
|
+
this.animationMixer.uncacheClip(runtimeClip.clip);
|
|
385
785
|
} catch {
|
|
386
786
|
}
|
|
787
|
+
this.bakedRuntimeActions.delete(runtimeClip.clip.name);
|
|
788
|
+
this.bakedRuntimeClipToSource.delete(runtimeClip.clip.name);
|
|
387
789
|
const actionId = this.getActionId(action);
|
|
388
|
-
if (actionId) {
|
|
790
|
+
if (actionId && action) {
|
|
389
791
|
this.actionIdToClip.delete(actionId);
|
|
792
|
+
this.actionIds.delete(action);
|
|
390
793
|
}
|
|
391
|
-
this.actionIds.delete(action);
|
|
392
794
|
}
|
|
393
795
|
}
|
|
394
796
|
this.animationClips = this.animationClips.filter((entry) => entry.name !== clipName);
|
|
395
|
-
this.
|
|
396
|
-
this.
|
|
397
|
-
this.clipHandles.delete(clipName);
|
|
398
|
-
this.animationFinishedCallbacks.delete(clipName);
|
|
797
|
+
this.bakedSourceClips.delete(clipName);
|
|
798
|
+
this.bakedActionGroups.delete(clipName);
|
|
399
799
|
this.playbackState.delete(clipName);
|
|
400
800
|
this.clipSources.delete(clipName);
|
|
401
801
|
return true;
|
|
402
802
|
}
|
|
403
803
|
playAnimation(clipName, options = {}) {
|
|
404
|
-
const
|
|
405
|
-
if (!
|
|
804
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
805
|
+
if (!bakedClip) {
|
|
406
806
|
console.warn(`Loom3: Animation clip "${clipName}" not found`);
|
|
407
807
|
return null;
|
|
408
808
|
}
|
|
409
|
-
if (!this.getActionId(action)) {
|
|
410
|
-
this.setActionId(action, clipName);
|
|
411
|
-
}
|
|
412
809
|
const playbackState = this.mergePlaybackOptions(
|
|
413
810
|
this.getPlaybackStateSnapshot(clipName, { loop: true, source: "baked" }),
|
|
414
811
|
options
|
|
415
812
|
);
|
|
813
|
+
playbackState.blendMode = this.getBakedAggregateBlendMode(clipName, playbackState);
|
|
814
|
+
const actionGroup = this.createBakedActionGroup(clipName, playbackState);
|
|
815
|
+
if (!actionGroup) {
|
|
816
|
+
console.warn(`Loom3: Animation clip "${clipName}" has no character-runtime channels to play`);
|
|
817
|
+
return null;
|
|
818
|
+
}
|
|
416
819
|
const crossfadeDuration = options.crossfadeDuration ?? 0;
|
|
417
820
|
const clampWhenFinished = options.clampWhenFinished ?? playbackState.loopMode === "once";
|
|
418
|
-
const startTime = this.resolveStartTime(
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
821
|
+
const startTime = this.resolveStartTime(bakedClip.sourceClip.duration, playbackState, options.startTime);
|
|
822
|
+
for (const [channel, action] of actionGroup.channelActions) {
|
|
823
|
+
this.applyPlaybackStateToBakedAction(action, playbackState, channel);
|
|
824
|
+
action.clampWhenFinished = clampWhenFinished;
|
|
825
|
+
if (crossfadeDuration > 0) {
|
|
826
|
+
action.reset();
|
|
827
|
+
action.fadeIn(crossfadeDuration);
|
|
828
|
+
} else {
|
|
829
|
+
action.reset();
|
|
830
|
+
}
|
|
831
|
+
action.time = startTime;
|
|
832
|
+
action.play();
|
|
426
833
|
}
|
|
427
|
-
|
|
428
|
-
action.play();
|
|
429
|
-
this.animationActions.set(clipName, action);
|
|
834
|
+
this.bakedActionGroups.set(clipName, actionGroup);
|
|
430
835
|
this.setPlaybackState(clipName, playbackState);
|
|
431
|
-
|
|
432
|
-
const finishedPromise = new Promise((resolve) => {
|
|
433
|
-
resolveFinished = resolve;
|
|
434
|
-
});
|
|
435
|
-
if (playbackState.loopMode === "once") {
|
|
436
|
-
this.animationFinishedCallbacks.set(clipName, () => resolveFinished());
|
|
437
|
-
}
|
|
438
|
-
return this.createAnimationHandle(clipName, action, finishedPromise);
|
|
836
|
+
return this.createBakedAnimationHandle(clipName, actionGroup);
|
|
439
837
|
}
|
|
440
838
|
stopAnimation(clipName) {
|
|
839
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
840
|
+
if (bakedGroup) {
|
|
841
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
842
|
+
action2.stop();
|
|
843
|
+
try {
|
|
844
|
+
action2.paused = false;
|
|
845
|
+
} catch {
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
this.bakedActionGroups.delete(clipName);
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
441
851
|
const action = this.animationActions.get(clipName);
|
|
442
852
|
if (action) {
|
|
853
|
+
const actionId = this.getActionId(action);
|
|
443
854
|
const isBaked = (this.clipSources.get(clipName) ?? "baked") === "baked";
|
|
444
855
|
action.stop();
|
|
445
856
|
if (!isBaked && this.animationMixer) {
|
|
@@ -462,9 +873,11 @@ var BakedAnimationController = class {
|
|
|
462
873
|
}
|
|
463
874
|
}
|
|
464
875
|
this.animationFinishedCallbacks.delete(clipName);
|
|
876
|
+
if (actionId) this.cleanupClipMonitor(actionId);
|
|
465
877
|
}
|
|
466
878
|
const clipAction = this.clipActions.get(clipName);
|
|
467
879
|
if (clipAction && clipAction !== action) {
|
|
880
|
+
const actionId = this.getActionId(clipAction);
|
|
468
881
|
try {
|
|
469
882
|
clipAction.stop();
|
|
470
883
|
if (this.animationMixer) {
|
|
@@ -477,6 +890,7 @@ var BakedAnimationController = class {
|
|
|
477
890
|
} catch {
|
|
478
891
|
}
|
|
479
892
|
this.clipActions.delete(clipName);
|
|
893
|
+
if (actionId) this.cleanupClipMonitor(actionId);
|
|
480
894
|
}
|
|
481
895
|
if (this.clipActions.get(clipName) === action) {
|
|
482
896
|
this.clipActions.delete(clipName);
|
|
@@ -485,6 +899,7 @@ var BakedAnimationController = class {
|
|
|
485
899
|
}
|
|
486
900
|
stopAllAnimations() {
|
|
487
901
|
for (const clipName of /* @__PURE__ */ new Set([
|
|
902
|
+
...this.bakedActionGroups.keys(),
|
|
488
903
|
...this.animationActions.keys(),
|
|
489
904
|
...this.clipActions.keys()
|
|
490
905
|
])) {
|
|
@@ -492,18 +907,39 @@ var BakedAnimationController = class {
|
|
|
492
907
|
}
|
|
493
908
|
}
|
|
494
909
|
pauseAnimation(clipName) {
|
|
910
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
911
|
+
if (bakedGroup) {
|
|
912
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
913
|
+
action2.paused = true;
|
|
914
|
+
}
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
495
917
|
const action = this.animationActions.get(clipName);
|
|
496
918
|
if (action) {
|
|
497
919
|
action.paused = true;
|
|
498
920
|
}
|
|
499
921
|
}
|
|
500
922
|
resumeAnimation(clipName) {
|
|
923
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
924
|
+
if (bakedGroup) {
|
|
925
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
926
|
+
action2.paused = false;
|
|
927
|
+
}
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
501
930
|
const action = this.animationActions.get(clipName);
|
|
502
931
|
if (action) {
|
|
503
932
|
action.paused = false;
|
|
504
933
|
}
|
|
505
934
|
}
|
|
506
935
|
pauseAllAnimations() {
|
|
936
|
+
for (const group of this.bakedActionGroups.values()) {
|
|
937
|
+
for (const action of group.channelActions.values()) {
|
|
938
|
+
if (action.isRunning()) {
|
|
939
|
+
action.paused = true;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
507
943
|
for (const action of this.animationActions.values()) {
|
|
508
944
|
if (action.isRunning()) {
|
|
509
945
|
action.paused = true;
|
|
@@ -511,6 +947,13 @@ var BakedAnimationController = class {
|
|
|
511
947
|
}
|
|
512
948
|
}
|
|
513
949
|
resumeAllAnimations() {
|
|
950
|
+
for (const group of this.bakedActionGroups.values()) {
|
|
951
|
+
for (const action of group.channelActions.values()) {
|
|
952
|
+
if (action.paused) {
|
|
953
|
+
action.paused = false;
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
514
957
|
for (const action of this.animationActions.values()) {
|
|
515
958
|
if (action.paused) {
|
|
516
959
|
action.paused = false;
|
|
@@ -518,76 +961,161 @@ var BakedAnimationController = class {
|
|
|
518
961
|
}
|
|
519
962
|
}
|
|
520
963
|
setAnimationSpeed(clipName, speed) {
|
|
521
|
-
|
|
522
|
-
if (action) {
|
|
964
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
523
965
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
524
966
|
loop: true,
|
|
525
967
|
source: this.clipSources.get(clipName) ?? "baked"
|
|
526
968
|
});
|
|
527
969
|
next.playbackRate = Number.isFinite(speed) ? Math.max(0, Math.abs(speed)) : 1;
|
|
970
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
971
|
+
if (bakedGroup) {
|
|
972
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
973
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
this.setPlaybackState(clipName, next);
|
|
977
|
+
return;
|
|
978
|
+
}
|
|
979
|
+
const action = this.animationActions.get(clipName);
|
|
980
|
+
if (action) {
|
|
981
|
+
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
982
|
+
loop: true,
|
|
983
|
+
source: this.clipSources.get(clipName) ?? "clip"
|
|
984
|
+
});
|
|
985
|
+
next.playbackRate = Number.isFinite(speed) ? Math.max(0, Math.abs(speed)) : 1;
|
|
528
986
|
this.applyPlaybackState(action, next);
|
|
529
987
|
this.setPlaybackState(clipName, next);
|
|
530
988
|
}
|
|
531
989
|
}
|
|
532
990
|
setAnimationIntensity(clipName, intensity) {
|
|
533
|
-
|
|
534
|
-
if (action) {
|
|
991
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
535
992
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
536
993
|
loop: true,
|
|
537
994
|
source: this.clipSources.get(clipName) ?? "baked"
|
|
538
995
|
});
|
|
539
996
|
next.weight = Number.isFinite(intensity) ? Math.max(0, intensity) : 1;
|
|
997
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
998
|
+
if (bakedGroup) {
|
|
999
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1000
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
this.setPlaybackState(clipName, next);
|
|
1004
|
+
return;
|
|
1005
|
+
}
|
|
1006
|
+
const action = this.animationActions.get(clipName);
|
|
1007
|
+
if (action) {
|
|
1008
|
+
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
1009
|
+
loop: true,
|
|
1010
|
+
source: this.clipSources.get(clipName) ?? "clip"
|
|
1011
|
+
});
|
|
1012
|
+
next.weight = Number.isFinite(intensity) ? Math.max(0, intensity) : 1;
|
|
540
1013
|
action.setEffectiveWeight(next.weight);
|
|
541
1014
|
this.setPlaybackState(clipName, next);
|
|
542
1015
|
}
|
|
543
1016
|
}
|
|
544
1017
|
setAnimationLoopMode(clipName, loopMode) {
|
|
545
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
546
|
-
if (!action) return;
|
|
547
1018
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
548
1019
|
loop: true,
|
|
549
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1020
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
550
1021
|
});
|
|
551
1022
|
next.loopMode = loopMode;
|
|
552
1023
|
next.loop = loopMode !== "once";
|
|
1024
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
1025
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1026
|
+
if (bakedGroup) {
|
|
1027
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1028
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
this.setPlaybackState(clipName, next);
|
|
1032
|
+
return;
|
|
1033
|
+
}
|
|
1034
|
+
const action = this.animationActions.get(clipName);
|
|
1035
|
+
if (!action) return;
|
|
553
1036
|
this.applyPlaybackState(action, next);
|
|
554
1037
|
this.setPlaybackState(clipName, next);
|
|
555
1038
|
}
|
|
556
1039
|
setAnimationRepeatCount(clipName, repeatCount) {
|
|
557
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
558
|
-
if (!action) return;
|
|
559
1040
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
560
1041
|
loop: true,
|
|
561
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1042
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
562
1043
|
});
|
|
563
1044
|
next.repeatCount = typeof repeatCount === "number" && Number.isFinite(repeatCount) ? Math.max(0, repeatCount) : void 0;
|
|
1045
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
1046
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1047
|
+
if (bakedGroup) {
|
|
1048
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1049
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
this.setPlaybackState(clipName, next);
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
const action = this.animationActions.get(clipName);
|
|
1056
|
+
if (!action) return;
|
|
564
1057
|
this.applyPlaybackState(action, next);
|
|
565
1058
|
this.setPlaybackState(clipName, next);
|
|
566
1059
|
}
|
|
567
1060
|
setAnimationReverse(clipName, reverse) {
|
|
568
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
569
|
-
if (!action) return;
|
|
570
1061
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
571
1062
|
loop: true,
|
|
572
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1063
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
573
1064
|
});
|
|
574
1065
|
next.reverse = !!reverse;
|
|
1066
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
1067
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1068
|
+
if (bakedGroup) {
|
|
1069
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1070
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
this.setPlaybackState(clipName, next);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
const action = this.animationActions.get(clipName);
|
|
1077
|
+
if (!action) return;
|
|
575
1078
|
this.applyPlaybackState(action, next);
|
|
576
1079
|
this.setPlaybackState(clipName, next);
|
|
577
1080
|
}
|
|
578
1081
|
setAnimationBlendMode(clipName, blendMode) {
|
|
579
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
580
|
-
if (!action) return;
|
|
581
1082
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
582
1083
|
loop: true,
|
|
583
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1084
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
584
1085
|
});
|
|
1086
|
+
next.requestedBlendMode = blendMode;
|
|
1087
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
1088
|
+
next.blendMode = this.getBakedAggregateBlendMode(clipName, next);
|
|
1089
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1090
|
+
if (bakedGroup) {
|
|
1091
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1092
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
this.setPlaybackState(clipName, next);
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
585
1098
|
next.blendMode = blendMode;
|
|
1099
|
+
const action = this.animationActions.get(clipName);
|
|
1100
|
+
if (!action) return;
|
|
586
1101
|
this.applyPlaybackState(action, next);
|
|
587
1102
|
this.setPlaybackState(clipName, next);
|
|
588
1103
|
}
|
|
589
1104
|
seekAnimation(clipName, time) {
|
|
590
|
-
const
|
|
1105
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1106
|
+
if (bakedGroup) {
|
|
1107
|
+
const duration2 = this.getBakedSourceClip(clipName)?.sourceClip.duration ?? 0;
|
|
1108
|
+
const clamped = Math.max(0, Math.min(duration2, Number.isFinite(time) ? time : 0));
|
|
1109
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
1110
|
+
action2.time = clamped;
|
|
1111
|
+
}
|
|
1112
|
+
try {
|
|
1113
|
+
this.animationMixer?.update(0);
|
|
1114
|
+
} catch {
|
|
1115
|
+
}
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
const action = this.animationActions.get(clipName);
|
|
591
1119
|
if (!action) return;
|
|
592
1120
|
const duration = action.getClip().duration;
|
|
593
1121
|
action.time = Math.max(0, Math.min(duration, Number.isFinite(time) ? time : 0));
|
|
@@ -602,6 +1130,40 @@ var BakedAnimationController = class {
|
|
|
602
1130
|
}
|
|
603
1131
|
}
|
|
604
1132
|
getAnimationState(clipName) {
|
|
1133
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
1134
|
+
if (bakedClip) {
|
|
1135
|
+
const state2 = this.playbackState.get(clipName);
|
|
1136
|
+
const action2 = this.getRepresentativeBakedAction(clipName);
|
|
1137
|
+
if (!state2 && !action2) {
|
|
1138
|
+
return null;
|
|
1139
|
+
}
|
|
1140
|
+
const loopMode2 = state2?.loopMode ?? (action2?.loop === LoopPingPong ? "pingpong" : action2?.loop === LoopOnce ? "once" : "repeat");
|
|
1141
|
+
const playbackRate2 = state2?.playbackRate ?? Math.abs(action2?.getEffectiveTimeScale?.() ?? 1);
|
|
1142
|
+
const reverse2 = state2?.reverse ?? (action2?.getEffectiveTimeScale?.() ?? 1) < 0;
|
|
1143
|
+
const pausedValues = this.bakedActionGroups.get(clipName) ? Array.from(this.bakedActionGroups.get(clipName).channelActions.values()).map((entry) => entry.paused) : [];
|
|
1144
|
+
return {
|
|
1145
|
+
name: bakedClip.sourceClip.name,
|
|
1146
|
+
actionId: this.bakedActionGroups.get(clipName)?.actionId,
|
|
1147
|
+
source: state2?.source ?? this.clipSources.get(clipName) ?? "baked",
|
|
1148
|
+
isPlaying: this.bakedActionGroups.get(clipName) ? Array.from(this.bakedActionGroups.get(clipName).channelActions.values()).some((entry) => entry.isRunning() && !entry.paused) : false,
|
|
1149
|
+
isPaused: pausedValues.length > 0 ? pausedValues.every(Boolean) : false,
|
|
1150
|
+
time: action2?.time ?? 0,
|
|
1151
|
+
duration: bakedClip.sourceClip.duration,
|
|
1152
|
+
speed: playbackRate2,
|
|
1153
|
+
playbackRate: playbackRate2,
|
|
1154
|
+
reverse: reverse2,
|
|
1155
|
+
weight: state2?.weight ?? action2?.getEffectiveWeight?.() ?? 1,
|
|
1156
|
+
balance: state2?.balance ?? 0,
|
|
1157
|
+
requestedBlendMode: state2?.requestedBlendMode ?? "replace",
|
|
1158
|
+
blendMode: this.getBakedAggregateBlendMode(clipName, state2),
|
|
1159
|
+
channels: this.getBakedChannelInfo(clipName, state2),
|
|
1160
|
+
easing: state2?.easing ?? "linear",
|
|
1161
|
+
loop: loopMode2 !== "once",
|
|
1162
|
+
loopMode: loopMode2,
|
|
1163
|
+
repeatCount: state2?.repeatCount,
|
|
1164
|
+
isLooping: loopMode2 !== "once"
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
605
1167
|
const action = this.animationActions.get(clipName);
|
|
606
1168
|
if (!action) return null;
|
|
607
1169
|
const clip = action.getClip();
|
|
@@ -622,7 +1184,9 @@ var BakedAnimationController = class {
|
|
|
622
1184
|
reverse,
|
|
623
1185
|
weight: state?.weight ?? action.getEffectiveWeight(),
|
|
624
1186
|
balance: state?.balance ?? 0,
|
|
1187
|
+
requestedBlendMode: state?.requestedBlendMode ?? state?.blendMode ?? "replace",
|
|
625
1188
|
blendMode: state?.blendMode ?? "replace",
|
|
1189
|
+
channels: state?.source === "baked" ? this.getBakedChannelInfo(clipName, state) : void 0,
|
|
626
1190
|
easing: state?.easing ?? "linear",
|
|
627
1191
|
loop: loopMode !== "once",
|
|
628
1192
|
loopMode,
|
|
@@ -632,6 +1196,12 @@ var BakedAnimationController = class {
|
|
|
632
1196
|
}
|
|
633
1197
|
getPlayingAnimations() {
|
|
634
1198
|
const playing = [];
|
|
1199
|
+
for (const name of this.bakedActionGroups.keys()) {
|
|
1200
|
+
const state = this.getAnimationState(name);
|
|
1201
|
+
if (state?.isPlaying) {
|
|
1202
|
+
playing.push(state);
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
635
1205
|
for (const [name, action] of this.animationActions) {
|
|
636
1206
|
if (action.isRunning()) {
|
|
637
1207
|
const state = this.getAnimationState(name);
|
|
@@ -641,6 +1211,13 @@ var BakedAnimationController = class {
|
|
|
641
1211
|
return playing;
|
|
642
1212
|
}
|
|
643
1213
|
crossfadeTo(clipName, duration = 0.3, options = {}) {
|
|
1214
|
+
for (const group of this.bakedActionGroups.values()) {
|
|
1215
|
+
for (const action of group.channelActions.values()) {
|
|
1216
|
+
if (action.isRunning()) {
|
|
1217
|
+
action.fadeOut(duration);
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
644
1221
|
for (const action of this.animationActions.values()) {
|
|
645
1222
|
if (action.isRunning()) {
|
|
646
1223
|
action.fadeOut(duration);
|
|
@@ -875,6 +1452,7 @@ var BakedAnimationController = class {
|
|
|
875
1452
|
return null;
|
|
876
1453
|
}
|
|
877
1454
|
const clip = new AnimationClip(clipName, maxTime, tracks);
|
|
1455
|
+
this.setClipEventMetadata(clip, { keyframeTimes });
|
|
878
1456
|
console.log(`[Loom3] snippetToClip: Created clip "${clipName}" with ${tracks.length} tracks, duration ${maxTime.toFixed(2)}s`);
|
|
879
1457
|
return clip;
|
|
880
1458
|
}
|
|
@@ -906,28 +1484,38 @@ var BakedAnimationController = class {
|
|
|
906
1484
|
this.animationClips.push(clip);
|
|
907
1485
|
}
|
|
908
1486
|
this.applyPlaybackState(action, playbackState);
|
|
1487
|
+
if (actionId) {
|
|
1488
|
+
this.cleanupClipMonitor(actionId);
|
|
1489
|
+
}
|
|
909
1490
|
let resolveFinished;
|
|
910
1491
|
const finishedPromise = new Promise((resolve) => {
|
|
911
1492
|
resolveFinished = resolve;
|
|
912
1493
|
});
|
|
913
|
-
const
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
1494
|
+
const keyframeTimes = this.getClipEventMetadata(clip).keyframeTimes;
|
|
1495
|
+
const initialDirection = playbackState.reverse ? -1 : 1;
|
|
1496
|
+
const monitor = {
|
|
1497
|
+
action,
|
|
1498
|
+
actionId,
|
|
1499
|
+
clip,
|
|
1500
|
+
clipName: clip.name,
|
|
1501
|
+
duration: clip.duration,
|
|
1502
|
+
keyframeTimes,
|
|
1503
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
1504
|
+
initialDirection,
|
|
1505
|
+
direction: initialDirection,
|
|
1506
|
+
iteration: 0,
|
|
1507
|
+
lastTime: Math.max(0, Math.min(clip.duration, action.time)),
|
|
1508
|
+
lastKeyframeIndex: this.getKeyframeIndex(keyframeTimes, action.time),
|
|
1509
|
+
loopMode: playbackState.loopMode,
|
|
1510
|
+
finishedPending: false,
|
|
1511
|
+
cleanedUp: false,
|
|
1512
|
+
resolveFinished
|
|
922
1513
|
};
|
|
923
|
-
this.
|
|
924
|
-
resolveFinished();
|
|
925
|
-
cleanup();
|
|
926
|
-
});
|
|
927
|
-
finishedPromise.catch(() => cleanup());
|
|
1514
|
+
this.clipMonitors.set(actionId, monitor);
|
|
928
1515
|
action.reset();
|
|
929
1516
|
action.time = startTime;
|
|
930
1517
|
action.play();
|
|
1518
|
+
this.resetClipMonitor(monitor, action.time);
|
|
931
1519
|
this.clipActions.set(clip.name, action);
|
|
932
1520
|
this.animationActions.set(clip.name, action);
|
|
933
1521
|
this.setPlaybackState(clip.name, playbackState);
|
|
@@ -945,6 +1533,7 @@ var BakedAnimationController = class {
|
|
|
945
1533
|
})
|
|
946
1534
|
);
|
|
947
1535
|
action.play();
|
|
1536
|
+
this.resetClipMonitor(monitor, action.time);
|
|
948
1537
|
},
|
|
949
1538
|
stop: () => {
|
|
950
1539
|
action.stop();
|
|
@@ -962,8 +1551,7 @@ var BakedAnimationController = class {
|
|
|
962
1551
|
this.animationActions.delete(clip.name);
|
|
963
1552
|
this.animationFinishedCallbacks.delete(clip.name);
|
|
964
1553
|
this.playbackState.delete(clip.name);
|
|
965
|
-
|
|
966
|
-
cleanup();
|
|
1554
|
+
this.cleanupClipMonitor(actionId);
|
|
967
1555
|
},
|
|
968
1556
|
pause: () => {
|
|
969
1557
|
action.paused = true;
|
|
@@ -981,6 +1569,8 @@ var BakedAnimationController = class {
|
|
|
981
1569
|
const next = this.playbackState.get(clip.name) ?? playbackState;
|
|
982
1570
|
next.playbackRate = Number.isFinite(r) ? Math.max(0, Math.abs(r)) : 1;
|
|
983
1571
|
this.applyPlaybackState(action, next);
|
|
1572
|
+
monitor.direction = next.reverse ? -1 : 1;
|
|
1573
|
+
monitor.initialDirection = monitor.direction;
|
|
984
1574
|
this.setPlaybackState(clip.name, next);
|
|
985
1575
|
},
|
|
986
1576
|
setLoop: (mode, repeatCount) => {
|
|
@@ -989,6 +1579,7 @@ var BakedAnimationController = class {
|
|
|
989
1579
|
next.loop = mode !== "once";
|
|
990
1580
|
next.repeatCount = repeatCount;
|
|
991
1581
|
this.applyPlaybackState(action, next);
|
|
1582
|
+
monitor.loopMode = mode;
|
|
992
1583
|
this.setPlaybackState(clip.name, next);
|
|
993
1584
|
},
|
|
994
1585
|
setTime: (t) => {
|
|
@@ -998,9 +1589,16 @@ var BakedAnimationController = class {
|
|
|
998
1589
|
this.animationMixer?.update(0);
|
|
999
1590
|
} catch {
|
|
1000
1591
|
}
|
|
1592
|
+
this.syncClipMonitorTime(monitor, clamped, true);
|
|
1001
1593
|
},
|
|
1002
1594
|
getTime: () => action.time,
|
|
1003
1595
|
getDuration: () => clip.duration,
|
|
1596
|
+
subscribe: (listener) => {
|
|
1597
|
+
monitor.listeners.add(listener);
|
|
1598
|
+
return () => {
|
|
1599
|
+
monitor.listeners.delete(listener);
|
|
1600
|
+
};
|
|
1601
|
+
},
|
|
1004
1602
|
finished: finishedPromise
|
|
1005
1603
|
};
|
|
1006
1604
|
this.clipHandles.set(clip.name, handle);
|
|
@@ -1024,6 +1622,7 @@ var BakedAnimationController = class {
|
|
|
1024
1622
|
if (!this.animationMixer || !this.host.getModel()) return;
|
|
1025
1623
|
for (const [clipName, action] of Array.from(this.clipActions.entries())) {
|
|
1026
1624
|
if (clipName === name || clipName.startsWith(`${name}_`)) {
|
|
1625
|
+
const actionId = this.getActionId(action);
|
|
1027
1626
|
try {
|
|
1028
1627
|
action.stop();
|
|
1029
1628
|
const clip = action.getClip();
|
|
@@ -1038,6 +1637,7 @@ var BakedAnimationController = class {
|
|
|
1038
1637
|
this.clipHandles.delete(clipName);
|
|
1039
1638
|
this.animationFinishedCallbacks.delete(clipName);
|
|
1040
1639
|
this.playbackState.delete(clipName);
|
|
1640
|
+
if (actionId) this.cleanupClipMonitor(actionId);
|
|
1041
1641
|
}
|
|
1042
1642
|
}
|
|
1043
1643
|
}
|
|
@@ -1061,6 +1661,8 @@ var BakedAnimationController = class {
|
|
|
1061
1661
|
console.log("[Loom3] updateClipParams start", debugSnapshot());
|
|
1062
1662
|
const apply = (action) => {
|
|
1063
1663
|
if (!action) return;
|
|
1664
|
+
const actionId = this.getActionId(action);
|
|
1665
|
+
const monitor = actionId ? this.clipMonitors.get(actionId) : void 0;
|
|
1064
1666
|
const clipName = action.getClip().name;
|
|
1065
1667
|
const next = this.playbackState.get(clipName) ?? this.normalizePlaybackOptions(void 0, { loop: false, source: this.clipSources.get(clipName) ?? "clip" });
|
|
1066
1668
|
try {
|
|
@@ -1079,6 +1681,10 @@ var BakedAnimationController = class {
|
|
|
1079
1681
|
}
|
|
1080
1682
|
const signedRate = next.reverse ? -next.playbackRate : next.playbackRate;
|
|
1081
1683
|
action.setEffectiveTimeScale(signedRate);
|
|
1684
|
+
if (monitor) {
|
|
1685
|
+
monitor.direction = next.reverse ? -1 : 1;
|
|
1686
|
+
monitor.initialDirection = monitor.direction;
|
|
1687
|
+
}
|
|
1082
1688
|
updated = true;
|
|
1083
1689
|
}
|
|
1084
1690
|
if (typeof params.loop === "boolean" || params.loopMode || params.repeatCount !== void 0) {
|
|
@@ -1086,6 +1692,7 @@ var BakedAnimationController = class {
|
|
|
1086
1692
|
next.loop = next.loopMode !== "once";
|
|
1087
1693
|
next.repeatCount = params.repeatCount;
|
|
1088
1694
|
this.applyPlaybackState(action, next);
|
|
1695
|
+
if (monitor) monitor.loopMode = next.loopMode;
|
|
1089
1696
|
updated = true;
|
|
1090
1697
|
}
|
|
1091
1698
|
this.setPlaybackState(clipName, next);
|
|
@@ -1165,7 +1772,23 @@ var BakedAnimationController = class {
|
|
|
1165
1772
|
if (this.animationMixer && !this.mixerFinishedListenerAttached) {
|
|
1166
1773
|
this.animationMixer.addEventListener("finished", (event) => {
|
|
1167
1774
|
const action = event.action;
|
|
1775
|
+
const actionId = this.getActionId(action);
|
|
1776
|
+
if (actionId) {
|
|
1777
|
+
const monitor = this.clipMonitors.get(actionId);
|
|
1778
|
+
if (monitor) {
|
|
1779
|
+
monitor.finishedPending = true;
|
|
1780
|
+
return;
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1168
1783
|
const clip = action.getClip();
|
|
1784
|
+
const bakedRuntime = this.bakedRuntimeClipToSource.get(clip.name);
|
|
1785
|
+
if (bakedRuntime) {
|
|
1786
|
+
const group = this.bakedActionGroups.get(bakedRuntime.sourceClipName);
|
|
1787
|
+
if (group && group.pendingFinishedChannels.delete(bakedRuntime.channel) && group.pendingFinishedChannels.size === 0) {
|
|
1788
|
+
group.resolveFinished();
|
|
1789
|
+
}
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1169
1792
|
const callback = this.animationFinishedCallbacks.get(clip.name);
|
|
1170
1793
|
if (callback) {
|
|
1171
1794
|
callback();
|
|
@@ -1190,6 +1813,20 @@ var BakedAnimationController = class {
|
|
|
1190
1813
|
finished: finishedPromise
|
|
1191
1814
|
};
|
|
1192
1815
|
}
|
|
1816
|
+
createBakedAnimationHandle(clipName, group) {
|
|
1817
|
+
return {
|
|
1818
|
+
actionId: group.actionId,
|
|
1819
|
+
stop: () => this.stopAnimation(clipName),
|
|
1820
|
+
pause: () => this.pauseAnimation(clipName),
|
|
1821
|
+
resume: () => this.resumeAnimation(clipName),
|
|
1822
|
+
setSpeed: (speed) => this.setAnimationSpeed(clipName, speed),
|
|
1823
|
+
setWeight: (weight) => this.setAnimationIntensity(clipName, weight),
|
|
1824
|
+
seekTo: (time) => this.seekAnimation(clipName, time),
|
|
1825
|
+
getState: () => this.getAnimationState(clipName),
|
|
1826
|
+
crossfadeTo: (targetClip, dur) => this.crossfadeTo(targetClip, dur),
|
|
1827
|
+
finished: group.finishedPromise
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1193
1830
|
};
|
|
1194
1831
|
|
|
1195
1832
|
// src/presets/cc4.ts
|
|
@@ -3849,9 +4486,11 @@ function getPreset(presetType) {
|
|
|
3849
4486
|
return CC4_PRESET;
|
|
3850
4487
|
}
|
|
3851
4488
|
}
|
|
4489
|
+
var resolvePreset = getPreset;
|
|
3852
4490
|
function getPresetWithProfile(presetType, profile) {
|
|
3853
4491
|
return extendPresetWithProfile(getPreset(presetType), profile);
|
|
3854
4492
|
}
|
|
4493
|
+
var resolvePresetWithOverrides = getPresetWithProfile;
|
|
3855
4494
|
|
|
3856
4495
|
// src/engines/three/Loom3.ts
|
|
3857
4496
|
var deg2rad = (d) => d * Math.PI / 180;
|
|
@@ -6959,6 +7598,6 @@ async function analyzeModel(options) {
|
|
|
6959
7598
|
};
|
|
6960
7599
|
}
|
|
6961
7600
|
|
|
6962
|
-
export { AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, AnimationThree, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, CC4_SUFFIX_PATTERN, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, DEFAULT_HAIR_PHYSICS_CONFIG, FISH_AU_MAPPING_CONFIG, HairPhysics, Loom3, Loom3 as Loom3Three, Loom3 as LoomLargeThree, MORPH_TO_MESH, VISEME_JAW_AMOUNTS, VISEME_KEYS, analyzeModel, applyCharacterProfileToPreset, collectMorphMeshes, detectFacingDirection, extendCharacterConfigWithPreset, extendPresetWithProfile, extractFromGLTF, extractModelData, extractProfileOverrides, findFaceCenter, fuzzyNameMatch, generateMappingCorrections, getModelForwardDirection, getPreset, getPresetWithProfile, hasLeftRightMorphs, isMixedAU, isPresetCompatible, mergeRegionsByName as mergeCharacterRegionsByName, resolveBoneName, resolveBoneNames, resolveFaceCenter, suggestBestPreset, validateMappingConfig, validateMappings };
|
|
7601
|
+
export { AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, AnimationThree, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, CC4_SUFFIX_PATTERN, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, DEFAULT_HAIR_PHYSICS_CONFIG, FISH_AU_MAPPING_CONFIG, HairPhysics, Loom3, Loom3 as Loom3Three, Loom3 as LoomLargeThree, MORPH_TO_MESH, VISEME_JAW_AMOUNTS, VISEME_KEYS, analyzeModel, applyCharacterProfileToPreset, collectMorphMeshes, detectFacingDirection, extendCharacterConfigWithPreset, extendPresetWithProfile, extractFromGLTF, extractModelData, extractProfileOverrides, findFaceCenter, fuzzyNameMatch, generateMappingCorrections, getModelForwardDirection, getPreset, getPresetWithProfile, hasLeftRightMorphs, isMixedAU, isPresetCompatible, mergeRegionsByName as mergeCharacterRegionsByName, resolveBoneName, resolveBoneNames, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, suggestBestPreset, validateMappingConfig, validateMappings };
|
|
6963
7602
|
//# sourceMappingURL=index.js.map
|
|
6964
7603
|
//# sourceMappingURL=index.js.map
|