@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.cjs
CHANGED
|
@@ -79,6 +79,119 @@ function resolveCurveBalance(curveId, globalBalance, balanceMap) {
|
|
|
79
79
|
}
|
|
80
80
|
return clampBalance(globalBalance);
|
|
81
81
|
}
|
|
82
|
+
var RUNTIME_CLIP_PREFIX = "__loom3_baked_partition__/";
|
|
83
|
+
var FACE_SAFE_TARGET_RE = /(head|neck|jaw|eye|brow|lid|mouth|lip|face|cheek|nose|tongue|teeth)/i;
|
|
84
|
+
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;
|
|
85
|
+
var SCENE_LIKE_TARGET_RE = /(camera|cam|scene|world|global|origin|pivot|cube)/i;
|
|
86
|
+
var CHANNEL_ORDER = ["face", "body", "scene"];
|
|
87
|
+
function getRuntimeClipName(sourceClipName, channel) {
|
|
88
|
+
return `${RUNTIME_CLIP_PREFIX}${sourceClipName}/${channel}`;
|
|
89
|
+
}
|
|
90
|
+
function parseTrackTarget(trackName, model) {
|
|
91
|
+
let parsed;
|
|
92
|
+
try {
|
|
93
|
+
parsed = THREE.PropertyBinding.parseTrackName(trackName);
|
|
94
|
+
} catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
const targetKey = parsed.objectName === "bones" && parsed.objectIndex ? String(parsed.objectIndex) : parsed.nodeName;
|
|
98
|
+
const target = targetKey ? model.getObjectByProperty("uuid", targetKey) ?? THREE.PropertyBinding.findNode(model, targetKey) : null;
|
|
99
|
+
return {
|
|
100
|
+
propertyName: parsed.propertyName,
|
|
101
|
+
target,
|
|
102
|
+
targetName: target?.name ?? parsed.nodeName ?? ""
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function isSceneTrackTarget(target, targetName) {
|
|
106
|
+
if (!target) return true;
|
|
107
|
+
if (target.isCamera) return true;
|
|
108
|
+
return SCENE_LIKE_TARGET_RE.test(targetName);
|
|
109
|
+
}
|
|
110
|
+
function isFaceSafeTransformTarget(target, targetName, safeTransformTargets) {
|
|
111
|
+
if (target && safeTransformTargets.has(target)) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
if (!targetName) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (BODY_LIKE_TARGET_RE.test(targetName) || SCENE_LIKE_TARGET_RE.test(targetName)) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
return FACE_SAFE_TARGET_RE.test(targetName);
|
|
121
|
+
}
|
|
122
|
+
function classifyBakedTrack(track, model, bones) {
|
|
123
|
+
const parsed = parseTrackTarget(track.name, model);
|
|
124
|
+
if (!parsed) {
|
|
125
|
+
return "scene";
|
|
126
|
+
}
|
|
127
|
+
if (parsed.propertyName === "morphTargetInfluences" || parsed.propertyName === "weights") {
|
|
128
|
+
return "face";
|
|
129
|
+
}
|
|
130
|
+
if (isSceneTrackTarget(parsed.target, parsed.targetName)) {
|
|
131
|
+
return "scene";
|
|
132
|
+
}
|
|
133
|
+
if (parsed.propertyName === "quaternion") {
|
|
134
|
+
const safeTransformTargets = new Set(
|
|
135
|
+
Object.values(bones).map((entry) => entry?.obj).filter((entry) => !!entry)
|
|
136
|
+
);
|
|
137
|
+
if (isFaceSafeTransformTarget(parsed.target, parsed.targetName, safeTransformTargets)) {
|
|
138
|
+
return "face";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return "body";
|
|
142
|
+
}
|
|
143
|
+
function resolveBakedChannelBlendMode(channel, requestedBlendMode) {
|
|
144
|
+
if (channel === "face") {
|
|
145
|
+
return requestedBlendMode === "additive" ? "additive" : "replace";
|
|
146
|
+
}
|
|
147
|
+
if (channel === "body") {
|
|
148
|
+
return "replace";
|
|
149
|
+
}
|
|
150
|
+
return void 0;
|
|
151
|
+
}
|
|
152
|
+
function resolveBakedAggregateBlendMode(channels, requestedBlendMode) {
|
|
153
|
+
if (requestedBlendMode !== "additive") {
|
|
154
|
+
return "replace";
|
|
155
|
+
}
|
|
156
|
+
return channels.some((channel) => channel.channel === "face" && channel.playable && channel.trackCount > 0) ? "additive" : "replace";
|
|
157
|
+
}
|
|
158
|
+
function partitionBakedClip(clip, model, bones) {
|
|
159
|
+
const tracksByChannel = new Map(
|
|
160
|
+
CHANNEL_ORDER.map((channel) => [channel, []])
|
|
161
|
+
);
|
|
162
|
+
for (const track of clip.tracks) {
|
|
163
|
+
const channel = classifyBakedTrack(track, model, bones);
|
|
164
|
+
tracksByChannel.get(channel)?.push(track.clone());
|
|
165
|
+
}
|
|
166
|
+
const runtimeClips = [];
|
|
167
|
+
const channels = [];
|
|
168
|
+
for (const channel of CHANNEL_ORDER) {
|
|
169
|
+
const tracks = tracksByChannel.get(channel) ?? [];
|
|
170
|
+
if (tracks.length === 0) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
const playable = channel !== "scene";
|
|
174
|
+
const blendMode = resolveBakedChannelBlendMode(channel, "additive");
|
|
175
|
+
channels.push({
|
|
176
|
+
channel,
|
|
177
|
+
trackCount: tracks.length,
|
|
178
|
+
playable,
|
|
179
|
+
blendMode
|
|
180
|
+
});
|
|
181
|
+
if (!playable) {
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
runtimeClips.push({
|
|
185
|
+
channel,
|
|
186
|
+
clip: new THREE.AnimationClip(getRuntimeClipName(clip.name, channel), clip.duration, tracks)
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
sourceClip: clip,
|
|
191
|
+
channels,
|
|
192
|
+
runtimeClips
|
|
193
|
+
};
|
|
194
|
+
}
|
|
82
195
|
|
|
83
196
|
// src/engines/three/AnimationThree.ts
|
|
84
197
|
var easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
@@ -178,12 +291,18 @@ var makeActionId = () => `act_${Math.random().toString(36).slice(2, 8)}_${Date.n
|
|
|
178
291
|
var X_AXIS = new THREE.Vector3(1, 0, 0);
|
|
179
292
|
var Y_AXIS = new THREE.Vector3(0, 1, 0);
|
|
180
293
|
var Z_AXIS = new THREE.Vector3(0, 0, 1);
|
|
294
|
+
var CLIP_EVENT_METADATA_KEY = "__loom3ClipEvents";
|
|
295
|
+
var CLIP_EVENT_EPSILON = 1e-4;
|
|
181
296
|
var BakedAnimationController = class {
|
|
182
297
|
constructor(host) {
|
|
183
298
|
__publicField(this, "host");
|
|
184
299
|
__publicField(this, "animationMixer", null);
|
|
185
300
|
__publicField(this, "mixerFinishedListenerAttached", false);
|
|
186
301
|
__publicField(this, "animationClips", []);
|
|
302
|
+
__publicField(this, "bakedSourceClips", /* @__PURE__ */ new Map());
|
|
303
|
+
__publicField(this, "bakedRuntimeActions", /* @__PURE__ */ new Map());
|
|
304
|
+
__publicField(this, "bakedActionGroups", /* @__PURE__ */ new Map());
|
|
305
|
+
__publicField(this, "bakedRuntimeClipToSource", /* @__PURE__ */ new Map());
|
|
187
306
|
__publicField(this, "animationActions", /* @__PURE__ */ new Map());
|
|
188
307
|
__publicField(this, "animationFinishedCallbacks", /* @__PURE__ */ new Map());
|
|
189
308
|
__publicField(this, "clipActions", /* @__PURE__ */ new Map());
|
|
@@ -192,6 +311,7 @@ var BakedAnimationController = class {
|
|
|
192
311
|
__publicField(this, "playbackState", /* @__PURE__ */ new Map());
|
|
193
312
|
__publicField(this, "actionIds", /* @__PURE__ */ new WeakMap());
|
|
194
313
|
__publicField(this, "actionIdToClip", /* @__PURE__ */ new Map());
|
|
314
|
+
__publicField(this, "clipMonitors", /* @__PURE__ */ new Map());
|
|
195
315
|
this.host = host;
|
|
196
316
|
}
|
|
197
317
|
getActionId(action) {
|
|
@@ -205,6 +325,161 @@ var BakedAnimationController = class {
|
|
|
205
325
|
action.__actionId = actionId;
|
|
206
326
|
return actionId;
|
|
207
327
|
}
|
|
328
|
+
setClipEventMetadata(clip, metadata) {
|
|
329
|
+
const userData = clip.userData ?? (clip.userData = {});
|
|
330
|
+
userData[CLIP_EVENT_METADATA_KEY] = metadata;
|
|
331
|
+
}
|
|
332
|
+
getClipEventMetadata(clip) {
|
|
333
|
+
const userData = clip.userData;
|
|
334
|
+
const keyframeTimes = Array.isArray(userData?.[CLIP_EVENT_METADATA_KEY]?.keyframeTimes) ? userData[CLIP_EVENT_METADATA_KEY].keyframeTimes.filter((time) => Number.isFinite(time)) : [];
|
|
335
|
+
return { keyframeTimes };
|
|
336
|
+
}
|
|
337
|
+
getKeyframeIndex(times, currentTime) {
|
|
338
|
+
if (!times.length) return -1;
|
|
339
|
+
const target = Math.max(0, currentTime) + 1e-3;
|
|
340
|
+
let lo = 0;
|
|
341
|
+
let hi = times.length - 1;
|
|
342
|
+
let idx = 0;
|
|
343
|
+
while (lo <= hi) {
|
|
344
|
+
const mid = lo + hi >>> 1;
|
|
345
|
+
if (times[mid] <= target) {
|
|
346
|
+
idx = mid;
|
|
347
|
+
lo = mid + 1;
|
|
348
|
+
} else {
|
|
349
|
+
hi = mid - 1;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return idx;
|
|
353
|
+
}
|
|
354
|
+
emitClipEvent(monitor, event) {
|
|
355
|
+
for (const listener of Array.from(monitor.listeners)) {
|
|
356
|
+
try {
|
|
357
|
+
listener(event);
|
|
358
|
+
} catch (error) {
|
|
359
|
+
console.error("[Loom3] clip event listener failed", error);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
emitKeyframesForRange(monitor, startTime, endTime, direction, includeStart) {
|
|
364
|
+
if (!monitor.keyframeTimes.length) return;
|
|
365
|
+
const times = direction === 1 ? monitor.keyframeTimes : [...monitor.keyframeTimes].reverse();
|
|
366
|
+
for (const time of times) {
|
|
367
|
+
const matchesForward = direction === 1 && (includeStart ? time >= startTime - CLIP_EVENT_EPSILON : time > startTime + CLIP_EVENT_EPSILON) && time <= endTime + CLIP_EVENT_EPSILON;
|
|
368
|
+
const matchesReverse = direction === -1 && (includeStart ? time <= startTime + CLIP_EVENT_EPSILON : time < startTime - CLIP_EVENT_EPSILON) && time >= endTime - CLIP_EVENT_EPSILON;
|
|
369
|
+
if (!matchesForward && !matchesReverse) continue;
|
|
370
|
+
const keyframeIndex = monitor.keyframeTimes.indexOf(time);
|
|
371
|
+
monitor.lastKeyframeIndex = keyframeIndex;
|
|
372
|
+
this.emitClipEvent(monitor, {
|
|
373
|
+
type: "keyframe",
|
|
374
|
+
clipName: monitor.clipName,
|
|
375
|
+
keyframeIndex,
|
|
376
|
+
totalKeyframes: monitor.keyframeTimes.length,
|
|
377
|
+
currentTime: time,
|
|
378
|
+
duration: monitor.duration,
|
|
379
|
+
iteration: monitor.iteration
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
resetClipMonitor(monitor, currentTime) {
|
|
384
|
+
monitor.iteration = 0;
|
|
385
|
+
monitor.direction = monitor.initialDirection;
|
|
386
|
+
monitor.lastTime = currentTime;
|
|
387
|
+
monitor.lastKeyframeIndex = this.getKeyframeIndex(monitor.keyframeTimes, currentTime);
|
|
388
|
+
monitor.finishedPending = false;
|
|
389
|
+
}
|
|
390
|
+
syncClipMonitorTime(monitor, currentTime, emitSeek = false) {
|
|
391
|
+
const clamped = Math.max(0, Math.min(monitor.duration, currentTime));
|
|
392
|
+
monitor.lastTime = clamped;
|
|
393
|
+
monitor.lastKeyframeIndex = this.getKeyframeIndex(monitor.keyframeTimes, clamped);
|
|
394
|
+
if (emitSeek) {
|
|
395
|
+
this.emitClipEvent(monitor, {
|
|
396
|
+
type: "seek",
|
|
397
|
+
clipName: monitor.clipName,
|
|
398
|
+
currentTime: clamped,
|
|
399
|
+
duration: monitor.duration,
|
|
400
|
+
iteration: monitor.iteration
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
cleanupClipMonitor(actionId) {
|
|
405
|
+
const monitor = this.clipMonitors.get(actionId);
|
|
406
|
+
if (!monitor || monitor.cleanedUp) return;
|
|
407
|
+
monitor.cleanedUp = true;
|
|
408
|
+
try {
|
|
409
|
+
monitor.action.paused = true;
|
|
410
|
+
} catch {
|
|
411
|
+
}
|
|
412
|
+
monitor.resolveFinished();
|
|
413
|
+
monitor.listeners.clear();
|
|
414
|
+
this.clipMonitors.delete(actionId);
|
|
415
|
+
this.actionIdToClip.delete(actionId);
|
|
416
|
+
}
|
|
417
|
+
advanceClipMonitor(monitor, previousTime) {
|
|
418
|
+
if (monitor.cleanedUp || monitor.action.paused && !monitor.finishedPending) return;
|
|
419
|
+
const currentTime = Math.max(0, Math.min(monitor.duration, monitor.action.time));
|
|
420
|
+
const delta = currentTime - previousTime;
|
|
421
|
+
if (monitor.loopMode === "pingpong") {
|
|
422
|
+
const movingForward = monitor.direction === 1;
|
|
423
|
+
const bouncedAtEnd = movingForward && delta < -CLIP_EVENT_EPSILON;
|
|
424
|
+
const bouncedAtStart = !movingForward && delta > CLIP_EVENT_EPSILON;
|
|
425
|
+
if (bouncedAtEnd) {
|
|
426
|
+
this.emitKeyframesForRange(monitor, previousTime, monitor.duration, 1, false);
|
|
427
|
+
monitor.direction = -1;
|
|
428
|
+
this.emitKeyframesForRange(monitor, monitor.duration, currentTime, -1, false);
|
|
429
|
+
} else if (bouncedAtStart) {
|
|
430
|
+
this.emitKeyframesForRange(monitor, previousTime, 0, -1, false);
|
|
431
|
+
monitor.direction = 1;
|
|
432
|
+
monitor.iteration += 1;
|
|
433
|
+
this.emitClipEvent(monitor, {
|
|
434
|
+
type: "loop",
|
|
435
|
+
clipName: monitor.clipName,
|
|
436
|
+
iteration: monitor.iteration,
|
|
437
|
+
currentTime: 0,
|
|
438
|
+
duration: monitor.duration
|
|
439
|
+
});
|
|
440
|
+
this.emitKeyframesForRange(monitor, 0, currentTime, 1, false);
|
|
441
|
+
} else if (delta > CLIP_EVENT_EPSILON) {
|
|
442
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, 1, false);
|
|
443
|
+
monitor.direction = 1;
|
|
444
|
+
} else if (delta < -CLIP_EVENT_EPSILON) {
|
|
445
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, -1, false);
|
|
446
|
+
monitor.direction = -1;
|
|
447
|
+
}
|
|
448
|
+
} else if (monitor.direction === 1) {
|
|
449
|
+
const wrapped = currentTime + CLIP_EVENT_EPSILON < previousTime;
|
|
450
|
+
if (wrapped) {
|
|
451
|
+
this.emitKeyframesForRange(monitor, previousTime, monitor.duration, 1, false);
|
|
452
|
+
monitor.iteration += 1;
|
|
453
|
+
this.emitClipEvent(monitor, {
|
|
454
|
+
type: "loop",
|
|
455
|
+
clipName: monitor.clipName,
|
|
456
|
+
iteration: monitor.iteration,
|
|
457
|
+
currentTime: 0,
|
|
458
|
+
duration: monitor.duration
|
|
459
|
+
});
|
|
460
|
+
this.emitKeyframesForRange(monitor, 0, currentTime, 1, true);
|
|
461
|
+
} else if (delta > CLIP_EVENT_EPSILON) {
|
|
462
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, 1, false);
|
|
463
|
+
}
|
|
464
|
+
} else {
|
|
465
|
+
const wrapped = currentTime > previousTime + CLIP_EVENT_EPSILON;
|
|
466
|
+
if (wrapped) {
|
|
467
|
+
this.emitKeyframesForRange(monitor, previousTime, 0, -1, false);
|
|
468
|
+
monitor.iteration += 1;
|
|
469
|
+
this.emitClipEvent(monitor, {
|
|
470
|
+
type: "loop",
|
|
471
|
+
clipName: monitor.clipName,
|
|
472
|
+
iteration: monitor.iteration,
|
|
473
|
+
currentTime: monitor.duration,
|
|
474
|
+
duration: monitor.duration
|
|
475
|
+
});
|
|
476
|
+
this.emitKeyframesForRange(monitor, monitor.duration, currentTime, -1, true);
|
|
477
|
+
} else if (delta < -CLIP_EVENT_EPSILON) {
|
|
478
|
+
this.emitKeyframesForRange(monitor, previousTime, currentTime, -1, false);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
this.syncClipMonitorTime(monitor, currentTime);
|
|
482
|
+
}
|
|
208
483
|
normalizePlaybackOptions(options, defaults) {
|
|
209
484
|
const clipOptions = options;
|
|
210
485
|
const rawRate = options?.playbackRate ?? options?.speed ?? 1;
|
|
@@ -212,6 +487,7 @@ var BakedAnimationController = class {
|
|
|
212
487
|
const rawWeight = options?.weight ?? options?.intensity ?? clipOptions?.mixerWeight ?? 1;
|
|
213
488
|
const weight = Number.isFinite(rawWeight) ? Math.max(0, rawWeight) : 1;
|
|
214
489
|
const loopMode = options?.loopMode ?? (typeof options?.loop === "boolean" ? options.loop ? "repeat" : "once" : defaults.loop ? "repeat" : "once");
|
|
490
|
+
const requestedBlendMode = options?.blendMode ?? (clipOptions?.mixerAdditive ? "additive" : "replace");
|
|
215
491
|
return {
|
|
216
492
|
source: options?.source ?? defaults.source,
|
|
217
493
|
loop: loopMode !== "once",
|
|
@@ -221,7 +497,8 @@ var BakedAnimationController = class {
|
|
|
221
497
|
playbackRate,
|
|
222
498
|
weight,
|
|
223
499
|
balance: Number.isFinite(options?.balance) ? options?.balance ?? 0 : 0,
|
|
224
|
-
|
|
500
|
+
requestedBlendMode,
|
|
501
|
+
blendMode: requestedBlendMode,
|
|
225
502
|
easing: options?.easing ?? "linear"
|
|
226
503
|
};
|
|
227
504
|
}
|
|
@@ -281,15 +558,49 @@ var BakedAnimationController = class {
|
|
|
281
558
|
next.balance = Math.max(-1, Math.min(1, options.balance));
|
|
282
559
|
}
|
|
283
560
|
if (options.blendMode) {
|
|
284
|
-
next.
|
|
561
|
+
next.requestedBlendMode = options.blendMode;
|
|
285
562
|
} else if (typeof clipOptions?.mixerAdditive === "boolean") {
|
|
286
|
-
next.
|
|
563
|
+
next.requestedBlendMode = clipOptions.mixerAdditive ? "additive" : "replace";
|
|
287
564
|
}
|
|
565
|
+
next.blendMode = next.requestedBlendMode;
|
|
288
566
|
if (options.easing) {
|
|
289
567
|
next.easing = options.easing;
|
|
290
568
|
}
|
|
291
569
|
return next;
|
|
292
570
|
}
|
|
571
|
+
isBakedSourceClip(clipName) {
|
|
572
|
+
return this.bakedSourceClips.has(clipName);
|
|
573
|
+
}
|
|
574
|
+
getBakedSourceClip(clipName) {
|
|
575
|
+
return this.bakedSourceClips.get(clipName);
|
|
576
|
+
}
|
|
577
|
+
getBakedChannelInfo(clipName, playbackState) {
|
|
578
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
579
|
+
if (!bakedClip) {
|
|
580
|
+
return void 0;
|
|
581
|
+
}
|
|
582
|
+
const requestedBlendMode = playbackState?.requestedBlendMode ?? "replace";
|
|
583
|
+
return bakedClip.channels.map((channel) => ({
|
|
584
|
+
...channel,
|
|
585
|
+
blendMode: resolveBakedChannelBlendMode(channel.channel, requestedBlendMode)
|
|
586
|
+
}));
|
|
587
|
+
}
|
|
588
|
+
getBakedAggregateBlendMode(clipName, playbackState) {
|
|
589
|
+
const channels = this.getBakedChannelInfo(clipName, playbackState);
|
|
590
|
+
if (!channels) {
|
|
591
|
+
return playbackState?.requestedBlendMode ?? playbackState?.blendMode ?? "replace";
|
|
592
|
+
}
|
|
593
|
+
return resolveBakedAggregateBlendMode(
|
|
594
|
+
channels,
|
|
595
|
+
playbackState?.requestedBlendMode ?? "replace"
|
|
596
|
+
);
|
|
597
|
+
}
|
|
598
|
+
applyPlaybackStateToBakedAction(action, state, channel) {
|
|
599
|
+
this.applyPlaybackState(action, {
|
|
600
|
+
...state,
|
|
601
|
+
blendMode: resolveBakedChannelBlendMode(channel, state.requestedBlendMode) ?? "replace"
|
|
602
|
+
});
|
|
603
|
+
}
|
|
293
604
|
resolveStartTime(duration, state, explicitStartTime) {
|
|
294
605
|
if (typeof explicitStartTime === "number" && Number.isFinite(explicitStartTime)) {
|
|
295
606
|
return Math.max(0, Math.min(duration, explicitStartTime));
|
|
@@ -299,8 +610,13 @@ var BakedAnimationController = class {
|
|
|
299
610
|
}
|
|
300
611
|
return 0;
|
|
301
612
|
}
|
|
302
|
-
|
|
303
|
-
const
|
|
613
|
+
getOrCreateBakedRuntimeAction(sourceClipName, channel) {
|
|
614
|
+
const bakedClip = this.getBakedSourceClip(sourceClipName);
|
|
615
|
+
const runtimeClip = bakedClip?.runtimeClips.find((entry) => entry.channel === channel)?.clip;
|
|
616
|
+
if (!runtimeClip) {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
const existing = this.bakedRuntimeActions.get(runtimeClip.name);
|
|
304
620
|
if (existing) {
|
|
305
621
|
return existing;
|
|
306
622
|
}
|
|
@@ -308,13 +624,44 @@ var BakedAnimationController = class {
|
|
|
308
624
|
if (!this.animationMixer) {
|
|
309
625
|
return null;
|
|
310
626
|
}
|
|
311
|
-
const
|
|
312
|
-
|
|
627
|
+
const action = this.animationMixer.clipAction(runtimeClip);
|
|
628
|
+
this.bakedRuntimeActions.set(runtimeClip.name, action);
|
|
629
|
+
return action;
|
|
630
|
+
}
|
|
631
|
+
getRepresentativeBakedAction(clipName) {
|
|
632
|
+
const group = this.bakedActionGroups.get(clipName);
|
|
633
|
+
if (!group) {
|
|
313
634
|
return null;
|
|
314
635
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
636
|
+
return group.channelActions.values().next().value ?? null;
|
|
637
|
+
}
|
|
638
|
+
createBakedActionGroup(clipName, playbackState) {
|
|
639
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
640
|
+
if (!bakedClip) {
|
|
641
|
+
return null;
|
|
642
|
+
}
|
|
643
|
+
const channelActions = /* @__PURE__ */ new Map();
|
|
644
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
645
|
+
const action = this.getOrCreateBakedRuntimeAction(clipName, runtimeClip.channel);
|
|
646
|
+
if (action) {
|
|
647
|
+
channelActions.set(runtimeClip.channel, action);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (channelActions.size === 0) {
|
|
651
|
+
return null;
|
|
652
|
+
}
|
|
653
|
+
let resolveFinished = () => {
|
|
654
|
+
};
|
|
655
|
+
const finishedPromise = new Promise((resolve) => {
|
|
656
|
+
resolveFinished = resolve;
|
|
657
|
+
});
|
|
658
|
+
return {
|
|
659
|
+
actionId: makeActionId(),
|
|
660
|
+
channelActions,
|
|
661
|
+
pendingFinishedChannels: playbackState.loopMode === "once" ? new Set(channelActions.keys()) : /* @__PURE__ */ new Set(),
|
|
662
|
+
finishedPromise,
|
|
663
|
+
resolveFinished
|
|
664
|
+
};
|
|
318
665
|
}
|
|
319
666
|
getMeshNamesForAU(auId, config, explicitMeshNames) {
|
|
320
667
|
if (explicitMeshNames && explicitMeshNames.length > 0) {
|
|
@@ -344,7 +691,28 @@ var BakedAnimationController = class {
|
|
|
344
691
|
}
|
|
345
692
|
update(dtSeconds) {
|
|
346
693
|
if (this.animationMixer) {
|
|
694
|
+
const snapshots = Array.from(this.clipMonitors.values()).map((monitor) => ({
|
|
695
|
+
actionId: monitor.actionId,
|
|
696
|
+
previousTime: monitor.action.time
|
|
697
|
+
}));
|
|
347
698
|
this.animationMixer.update(dtSeconds);
|
|
699
|
+
for (const { actionId, previousTime } of snapshots) {
|
|
700
|
+
const monitor = this.clipMonitors.get(actionId);
|
|
701
|
+
if (!monitor) continue;
|
|
702
|
+
this.advanceClipMonitor(monitor, previousTime);
|
|
703
|
+
if (monitor.finishedPending) {
|
|
704
|
+
const finalTime = Math.max(0, Math.min(monitor.duration, monitor.action.time));
|
|
705
|
+
this.syncClipMonitorTime(monitor, finalTime);
|
|
706
|
+
this.emitClipEvent(monitor, {
|
|
707
|
+
type: "completed",
|
|
708
|
+
clipName: monitor.clipName,
|
|
709
|
+
currentTime: finalTime,
|
|
710
|
+
duration: monitor.duration,
|
|
711
|
+
iteration: monitor.iteration
|
|
712
|
+
});
|
|
713
|
+
this.cleanupClipMonitor(actionId);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
348
716
|
}
|
|
349
717
|
}
|
|
350
718
|
dispose() {
|
|
@@ -354,25 +722,60 @@ var BakedAnimationController = class {
|
|
|
354
722
|
this.animationMixer = null;
|
|
355
723
|
}
|
|
356
724
|
this.animationClips = [];
|
|
725
|
+
this.bakedSourceClips.clear();
|
|
726
|
+
this.bakedRuntimeActions.clear();
|
|
727
|
+
this.bakedActionGroups.clear();
|
|
728
|
+
this.bakedRuntimeClipToSource.clear();
|
|
357
729
|
this.animationActions.clear();
|
|
358
730
|
this.animationFinishedCallbacks.clear();
|
|
359
731
|
this.clipActions.clear();
|
|
360
732
|
this.clipHandles.clear();
|
|
361
733
|
this.clipSources.clear();
|
|
362
734
|
this.playbackState.clear();
|
|
735
|
+
this.clipMonitors.clear();
|
|
363
736
|
}
|
|
364
737
|
loadAnimationClips(clips) {
|
|
365
|
-
|
|
738
|
+
const model = this.host.getModel();
|
|
739
|
+
if (!model) {
|
|
366
740
|
console.warn("Loom3: Cannot load animation clips before calling onReady()");
|
|
367
741
|
return;
|
|
368
742
|
}
|
|
743
|
+
for (const clipName of this.bakedSourceClips.keys()) {
|
|
744
|
+
this.stopAnimation(clipName);
|
|
745
|
+
}
|
|
746
|
+
if (this.animationMixer) {
|
|
747
|
+
for (const bakedClip of this.bakedSourceClips.values()) {
|
|
748
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
749
|
+
try {
|
|
750
|
+
this.animationMixer.uncacheAction(runtimeClip.clip);
|
|
751
|
+
} catch {
|
|
752
|
+
}
|
|
753
|
+
try {
|
|
754
|
+
this.animationMixer.uncacheClip(runtimeClip.clip);
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
for (const clipName of this.bakedSourceClips.keys()) {
|
|
761
|
+
this.playbackState.delete(clipName);
|
|
762
|
+
this.clipSources.delete(clipName);
|
|
763
|
+
}
|
|
764
|
+
this.bakedSourceClips.clear();
|
|
765
|
+
this.bakedRuntimeActions.clear();
|
|
766
|
+
this.bakedActionGroups.clear();
|
|
767
|
+
this.bakedRuntimeClipToSource.clear();
|
|
369
768
|
this.ensureMixer();
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
769
|
+
const partitionedClips = clips.map((clip) => partitionBakedClip(clip, model, this.host.getBones()));
|
|
770
|
+
this.animationClips = partitionedClips.map((clip) => clip.sourceClip);
|
|
771
|
+
for (const bakedClip of partitionedClips) {
|
|
772
|
+
this.bakedSourceClips.set(bakedClip.sourceClip.name, bakedClip);
|
|
773
|
+
this.clipSources.set(bakedClip.sourceClip.name, "baked");
|
|
774
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
775
|
+
this.bakedRuntimeClipToSource.set(runtimeClip.clip.name, {
|
|
776
|
+
sourceClipName: bakedClip.sourceClip.name,
|
|
777
|
+
channel: runtimeClip.channel
|
|
778
|
+
});
|
|
376
779
|
}
|
|
377
780
|
}
|
|
378
781
|
}
|
|
@@ -381,86 +784,94 @@ var BakedAnimationController = class {
|
|
|
381
784
|
name: clip.name,
|
|
382
785
|
duration: clip.duration,
|
|
383
786
|
trackCount: clip.tracks.length,
|
|
384
|
-
source: this.clipSources.get(clip.name) ?? "baked"
|
|
787
|
+
source: this.clipSources.get(clip.name) ?? "baked",
|
|
788
|
+
channels: this.getBakedSourceClip(clip.name)?.channels
|
|
385
789
|
}));
|
|
386
790
|
}
|
|
387
791
|
removeAnimationClip(clipName) {
|
|
388
|
-
const
|
|
389
|
-
if (!
|
|
792
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
793
|
+
if (!bakedClip) {
|
|
390
794
|
return false;
|
|
391
795
|
}
|
|
392
|
-
const relatedActions = /* @__PURE__ */ new Set();
|
|
393
|
-
const bakedAction = this.animationActions.get(clipName);
|
|
394
|
-
const clipAction = this.clipActions.get(clipName);
|
|
395
|
-
if (bakedAction) relatedActions.add(bakedAction);
|
|
396
|
-
if (clipAction) relatedActions.add(clipAction);
|
|
397
796
|
this.stopAnimation(clipName);
|
|
398
797
|
if (this.animationMixer) {
|
|
399
|
-
for (const
|
|
798
|
+
for (const runtimeClip of bakedClip.runtimeClips) {
|
|
799
|
+
const action = this.bakedRuntimeActions.get(runtimeClip.clip.name);
|
|
400
800
|
try {
|
|
401
|
-
this.animationMixer.uncacheAction(clip);
|
|
801
|
+
this.animationMixer.uncacheAction(runtimeClip.clip);
|
|
402
802
|
} catch {
|
|
403
803
|
}
|
|
404
804
|
try {
|
|
405
|
-
this.animationMixer.uncacheClip(clip);
|
|
805
|
+
this.animationMixer.uncacheClip(runtimeClip.clip);
|
|
406
806
|
} catch {
|
|
407
807
|
}
|
|
808
|
+
this.bakedRuntimeActions.delete(runtimeClip.clip.name);
|
|
809
|
+
this.bakedRuntimeClipToSource.delete(runtimeClip.clip.name);
|
|
408
810
|
const actionId = this.getActionId(action);
|
|
409
|
-
if (actionId) {
|
|
811
|
+
if (actionId && action) {
|
|
410
812
|
this.actionIdToClip.delete(actionId);
|
|
813
|
+
this.actionIds.delete(action);
|
|
411
814
|
}
|
|
412
|
-
this.actionIds.delete(action);
|
|
413
815
|
}
|
|
414
816
|
}
|
|
415
817
|
this.animationClips = this.animationClips.filter((entry) => entry.name !== clipName);
|
|
416
|
-
this.
|
|
417
|
-
this.
|
|
418
|
-
this.clipHandles.delete(clipName);
|
|
419
|
-
this.animationFinishedCallbacks.delete(clipName);
|
|
818
|
+
this.bakedSourceClips.delete(clipName);
|
|
819
|
+
this.bakedActionGroups.delete(clipName);
|
|
420
820
|
this.playbackState.delete(clipName);
|
|
421
821
|
this.clipSources.delete(clipName);
|
|
422
822
|
return true;
|
|
423
823
|
}
|
|
424
824
|
playAnimation(clipName, options = {}) {
|
|
425
|
-
const
|
|
426
|
-
if (!
|
|
825
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
826
|
+
if (!bakedClip) {
|
|
427
827
|
console.warn(`Loom3: Animation clip "${clipName}" not found`);
|
|
428
828
|
return null;
|
|
429
829
|
}
|
|
430
|
-
if (!this.getActionId(action)) {
|
|
431
|
-
this.setActionId(action, clipName);
|
|
432
|
-
}
|
|
433
830
|
const playbackState = this.mergePlaybackOptions(
|
|
434
831
|
this.getPlaybackStateSnapshot(clipName, { loop: true, source: "baked" }),
|
|
435
832
|
options
|
|
436
833
|
);
|
|
834
|
+
playbackState.blendMode = this.getBakedAggregateBlendMode(clipName, playbackState);
|
|
835
|
+
const actionGroup = this.createBakedActionGroup(clipName, playbackState);
|
|
836
|
+
if (!actionGroup) {
|
|
837
|
+
console.warn(`Loom3: Animation clip "${clipName}" has no character-runtime channels to play`);
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
437
840
|
const crossfadeDuration = options.crossfadeDuration ?? 0;
|
|
438
841
|
const clampWhenFinished = options.clampWhenFinished ?? playbackState.loopMode === "once";
|
|
439
|
-
const startTime = this.resolveStartTime(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
842
|
+
const startTime = this.resolveStartTime(bakedClip.sourceClip.duration, playbackState, options.startTime);
|
|
843
|
+
for (const [channel, action] of actionGroup.channelActions) {
|
|
844
|
+
this.applyPlaybackStateToBakedAction(action, playbackState, channel);
|
|
845
|
+
action.clampWhenFinished = clampWhenFinished;
|
|
846
|
+
if (crossfadeDuration > 0) {
|
|
847
|
+
action.reset();
|
|
848
|
+
action.fadeIn(crossfadeDuration);
|
|
849
|
+
} else {
|
|
850
|
+
action.reset();
|
|
851
|
+
}
|
|
852
|
+
action.time = startTime;
|
|
853
|
+
action.play();
|
|
447
854
|
}
|
|
448
|
-
|
|
449
|
-
action.play();
|
|
450
|
-
this.animationActions.set(clipName, action);
|
|
855
|
+
this.bakedActionGroups.set(clipName, actionGroup);
|
|
451
856
|
this.setPlaybackState(clipName, playbackState);
|
|
452
|
-
|
|
453
|
-
const finishedPromise = new Promise((resolve) => {
|
|
454
|
-
resolveFinished = resolve;
|
|
455
|
-
});
|
|
456
|
-
if (playbackState.loopMode === "once") {
|
|
457
|
-
this.animationFinishedCallbacks.set(clipName, () => resolveFinished());
|
|
458
|
-
}
|
|
459
|
-
return this.createAnimationHandle(clipName, action, finishedPromise);
|
|
857
|
+
return this.createBakedAnimationHandle(clipName, actionGroup);
|
|
460
858
|
}
|
|
461
859
|
stopAnimation(clipName) {
|
|
860
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
861
|
+
if (bakedGroup) {
|
|
862
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
863
|
+
action2.stop();
|
|
864
|
+
try {
|
|
865
|
+
action2.paused = false;
|
|
866
|
+
} catch {
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
this.bakedActionGroups.delete(clipName);
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
462
872
|
const action = this.animationActions.get(clipName);
|
|
463
873
|
if (action) {
|
|
874
|
+
const actionId = this.getActionId(action);
|
|
464
875
|
const isBaked = (this.clipSources.get(clipName) ?? "baked") === "baked";
|
|
465
876
|
action.stop();
|
|
466
877
|
if (!isBaked && this.animationMixer) {
|
|
@@ -483,9 +894,11 @@ var BakedAnimationController = class {
|
|
|
483
894
|
}
|
|
484
895
|
}
|
|
485
896
|
this.animationFinishedCallbacks.delete(clipName);
|
|
897
|
+
if (actionId) this.cleanupClipMonitor(actionId);
|
|
486
898
|
}
|
|
487
899
|
const clipAction = this.clipActions.get(clipName);
|
|
488
900
|
if (clipAction && clipAction !== action) {
|
|
901
|
+
const actionId = this.getActionId(clipAction);
|
|
489
902
|
try {
|
|
490
903
|
clipAction.stop();
|
|
491
904
|
if (this.animationMixer) {
|
|
@@ -498,6 +911,7 @@ var BakedAnimationController = class {
|
|
|
498
911
|
} catch {
|
|
499
912
|
}
|
|
500
913
|
this.clipActions.delete(clipName);
|
|
914
|
+
if (actionId) this.cleanupClipMonitor(actionId);
|
|
501
915
|
}
|
|
502
916
|
if (this.clipActions.get(clipName) === action) {
|
|
503
917
|
this.clipActions.delete(clipName);
|
|
@@ -506,6 +920,7 @@ var BakedAnimationController = class {
|
|
|
506
920
|
}
|
|
507
921
|
stopAllAnimations() {
|
|
508
922
|
for (const clipName of /* @__PURE__ */ new Set([
|
|
923
|
+
...this.bakedActionGroups.keys(),
|
|
509
924
|
...this.animationActions.keys(),
|
|
510
925
|
...this.clipActions.keys()
|
|
511
926
|
])) {
|
|
@@ -513,18 +928,39 @@ var BakedAnimationController = class {
|
|
|
513
928
|
}
|
|
514
929
|
}
|
|
515
930
|
pauseAnimation(clipName) {
|
|
931
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
932
|
+
if (bakedGroup) {
|
|
933
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
934
|
+
action2.paused = true;
|
|
935
|
+
}
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
516
938
|
const action = this.animationActions.get(clipName);
|
|
517
939
|
if (action) {
|
|
518
940
|
action.paused = true;
|
|
519
941
|
}
|
|
520
942
|
}
|
|
521
943
|
resumeAnimation(clipName) {
|
|
944
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
945
|
+
if (bakedGroup) {
|
|
946
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
947
|
+
action2.paused = false;
|
|
948
|
+
}
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
522
951
|
const action = this.animationActions.get(clipName);
|
|
523
952
|
if (action) {
|
|
524
953
|
action.paused = false;
|
|
525
954
|
}
|
|
526
955
|
}
|
|
527
956
|
pauseAllAnimations() {
|
|
957
|
+
for (const group of this.bakedActionGroups.values()) {
|
|
958
|
+
for (const action of group.channelActions.values()) {
|
|
959
|
+
if (action.isRunning()) {
|
|
960
|
+
action.paused = true;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
528
964
|
for (const action of this.animationActions.values()) {
|
|
529
965
|
if (action.isRunning()) {
|
|
530
966
|
action.paused = true;
|
|
@@ -532,6 +968,13 @@ var BakedAnimationController = class {
|
|
|
532
968
|
}
|
|
533
969
|
}
|
|
534
970
|
resumeAllAnimations() {
|
|
971
|
+
for (const group of this.bakedActionGroups.values()) {
|
|
972
|
+
for (const action of group.channelActions.values()) {
|
|
973
|
+
if (action.paused) {
|
|
974
|
+
action.paused = false;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
535
978
|
for (const action of this.animationActions.values()) {
|
|
536
979
|
if (action.paused) {
|
|
537
980
|
action.paused = false;
|
|
@@ -539,76 +982,161 @@ var BakedAnimationController = class {
|
|
|
539
982
|
}
|
|
540
983
|
}
|
|
541
984
|
setAnimationSpeed(clipName, speed) {
|
|
542
|
-
|
|
543
|
-
if (action) {
|
|
985
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
544
986
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
545
987
|
loop: true,
|
|
546
988
|
source: this.clipSources.get(clipName) ?? "baked"
|
|
547
989
|
});
|
|
548
990
|
next.playbackRate = Number.isFinite(speed) ? Math.max(0, Math.abs(speed)) : 1;
|
|
991
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
992
|
+
if (bakedGroup) {
|
|
993
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
994
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
this.setPlaybackState(clipName, next);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const action = this.animationActions.get(clipName);
|
|
1001
|
+
if (action) {
|
|
1002
|
+
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
1003
|
+
loop: true,
|
|
1004
|
+
source: this.clipSources.get(clipName) ?? "clip"
|
|
1005
|
+
});
|
|
1006
|
+
next.playbackRate = Number.isFinite(speed) ? Math.max(0, Math.abs(speed)) : 1;
|
|
549
1007
|
this.applyPlaybackState(action, next);
|
|
550
1008
|
this.setPlaybackState(clipName, next);
|
|
551
1009
|
}
|
|
552
1010
|
}
|
|
553
1011
|
setAnimationIntensity(clipName, intensity) {
|
|
554
|
-
|
|
555
|
-
if (action) {
|
|
1012
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
556
1013
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
557
1014
|
loop: true,
|
|
558
1015
|
source: this.clipSources.get(clipName) ?? "baked"
|
|
559
1016
|
});
|
|
560
1017
|
next.weight = Number.isFinite(intensity) ? Math.max(0, intensity) : 1;
|
|
1018
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1019
|
+
if (bakedGroup) {
|
|
1020
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1021
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
this.setPlaybackState(clipName, next);
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
const action = this.animationActions.get(clipName);
|
|
1028
|
+
if (action) {
|
|
1029
|
+
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
1030
|
+
loop: true,
|
|
1031
|
+
source: this.clipSources.get(clipName) ?? "clip"
|
|
1032
|
+
});
|
|
1033
|
+
next.weight = Number.isFinite(intensity) ? Math.max(0, intensity) : 1;
|
|
561
1034
|
action.setEffectiveWeight(next.weight);
|
|
562
1035
|
this.setPlaybackState(clipName, next);
|
|
563
1036
|
}
|
|
564
1037
|
}
|
|
565
1038
|
setAnimationLoopMode(clipName, loopMode) {
|
|
566
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
567
|
-
if (!action) return;
|
|
568
1039
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
569
1040
|
loop: true,
|
|
570
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1041
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
571
1042
|
});
|
|
572
1043
|
next.loopMode = loopMode;
|
|
573
1044
|
next.loop = loopMode !== "once";
|
|
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;
|
|
574
1057
|
this.applyPlaybackState(action, next);
|
|
575
1058
|
this.setPlaybackState(clipName, next);
|
|
576
1059
|
}
|
|
577
1060
|
setAnimationRepeatCount(clipName, repeatCount) {
|
|
578
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
579
|
-
if (!action) return;
|
|
580
1061
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
581
1062
|
loop: true,
|
|
582
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1063
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
583
1064
|
});
|
|
584
1065
|
next.repeatCount = typeof repeatCount === "number" && Number.isFinite(repeatCount) ? Math.max(0, repeatCount) : void 0;
|
|
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;
|
|
585
1078
|
this.applyPlaybackState(action, next);
|
|
586
1079
|
this.setPlaybackState(clipName, next);
|
|
587
1080
|
}
|
|
588
1081
|
setAnimationReverse(clipName, reverse) {
|
|
589
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
590
|
-
if (!action) return;
|
|
591
1082
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
592
1083
|
loop: true,
|
|
593
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1084
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
594
1085
|
});
|
|
595
1086
|
next.reverse = !!reverse;
|
|
1087
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
1088
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1089
|
+
if (bakedGroup) {
|
|
1090
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1091
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
this.setPlaybackState(clipName, next);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
const action = this.animationActions.get(clipName);
|
|
1098
|
+
if (!action) return;
|
|
596
1099
|
this.applyPlaybackState(action, next);
|
|
597
1100
|
this.setPlaybackState(clipName, next);
|
|
598
1101
|
}
|
|
599
1102
|
setAnimationBlendMode(clipName, blendMode) {
|
|
600
|
-
const action = this.getOrCreateBakedAction(clipName);
|
|
601
|
-
if (!action) return;
|
|
602
1103
|
const next = this.getPlaybackStateSnapshot(clipName, {
|
|
603
1104
|
loop: true,
|
|
604
|
-
source: this.clipSources.get(clipName) ?? "baked"
|
|
1105
|
+
source: this.clipSources.get(clipName) ?? (this.isBakedSourceClip(clipName) ? "baked" : "clip")
|
|
605
1106
|
});
|
|
1107
|
+
next.requestedBlendMode = blendMode;
|
|
1108
|
+
if (this.isBakedSourceClip(clipName)) {
|
|
1109
|
+
next.blendMode = this.getBakedAggregateBlendMode(clipName, next);
|
|
1110
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1111
|
+
if (bakedGroup) {
|
|
1112
|
+
for (const [channel, action2] of bakedGroup.channelActions) {
|
|
1113
|
+
this.applyPlaybackStateToBakedAction(action2, next, channel);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
this.setPlaybackState(clipName, next);
|
|
1117
|
+
return;
|
|
1118
|
+
}
|
|
606
1119
|
next.blendMode = blendMode;
|
|
1120
|
+
const action = this.animationActions.get(clipName);
|
|
1121
|
+
if (!action) return;
|
|
607
1122
|
this.applyPlaybackState(action, next);
|
|
608
1123
|
this.setPlaybackState(clipName, next);
|
|
609
1124
|
}
|
|
610
1125
|
seekAnimation(clipName, time) {
|
|
611
|
-
const
|
|
1126
|
+
const bakedGroup = this.bakedActionGroups.get(clipName);
|
|
1127
|
+
if (bakedGroup) {
|
|
1128
|
+
const duration2 = this.getBakedSourceClip(clipName)?.sourceClip.duration ?? 0;
|
|
1129
|
+
const clamped = Math.max(0, Math.min(duration2, Number.isFinite(time) ? time : 0));
|
|
1130
|
+
for (const action2 of bakedGroup.channelActions.values()) {
|
|
1131
|
+
action2.time = clamped;
|
|
1132
|
+
}
|
|
1133
|
+
try {
|
|
1134
|
+
this.animationMixer?.update(0);
|
|
1135
|
+
} catch {
|
|
1136
|
+
}
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
const action = this.animationActions.get(clipName);
|
|
612
1140
|
if (!action) return;
|
|
613
1141
|
const duration = action.getClip().duration;
|
|
614
1142
|
action.time = Math.max(0, Math.min(duration, Number.isFinite(time) ? time : 0));
|
|
@@ -623,6 +1151,40 @@ var BakedAnimationController = class {
|
|
|
623
1151
|
}
|
|
624
1152
|
}
|
|
625
1153
|
getAnimationState(clipName) {
|
|
1154
|
+
const bakedClip = this.getBakedSourceClip(clipName);
|
|
1155
|
+
if (bakedClip) {
|
|
1156
|
+
const state2 = this.playbackState.get(clipName);
|
|
1157
|
+
const action2 = this.getRepresentativeBakedAction(clipName);
|
|
1158
|
+
if (!state2 && !action2) {
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
const loopMode2 = state2?.loopMode ?? (action2?.loop === THREE.LoopPingPong ? "pingpong" : action2?.loop === THREE.LoopOnce ? "once" : "repeat");
|
|
1162
|
+
const playbackRate2 = state2?.playbackRate ?? Math.abs(action2?.getEffectiveTimeScale?.() ?? 1);
|
|
1163
|
+
const reverse2 = state2?.reverse ?? (action2?.getEffectiveTimeScale?.() ?? 1) < 0;
|
|
1164
|
+
const pausedValues = this.bakedActionGroups.get(clipName) ? Array.from(this.bakedActionGroups.get(clipName).channelActions.values()).map((entry) => entry.paused) : [];
|
|
1165
|
+
return {
|
|
1166
|
+
name: bakedClip.sourceClip.name,
|
|
1167
|
+
actionId: this.bakedActionGroups.get(clipName)?.actionId,
|
|
1168
|
+
source: state2?.source ?? this.clipSources.get(clipName) ?? "baked",
|
|
1169
|
+
isPlaying: this.bakedActionGroups.get(clipName) ? Array.from(this.bakedActionGroups.get(clipName).channelActions.values()).some((entry) => entry.isRunning() && !entry.paused) : false,
|
|
1170
|
+
isPaused: pausedValues.length > 0 ? pausedValues.every(Boolean) : false,
|
|
1171
|
+
time: action2?.time ?? 0,
|
|
1172
|
+
duration: bakedClip.sourceClip.duration,
|
|
1173
|
+
speed: playbackRate2,
|
|
1174
|
+
playbackRate: playbackRate2,
|
|
1175
|
+
reverse: reverse2,
|
|
1176
|
+
weight: state2?.weight ?? action2?.getEffectiveWeight?.() ?? 1,
|
|
1177
|
+
balance: state2?.balance ?? 0,
|
|
1178
|
+
requestedBlendMode: state2?.requestedBlendMode ?? "replace",
|
|
1179
|
+
blendMode: this.getBakedAggregateBlendMode(clipName, state2),
|
|
1180
|
+
channels: this.getBakedChannelInfo(clipName, state2),
|
|
1181
|
+
easing: state2?.easing ?? "linear",
|
|
1182
|
+
loop: loopMode2 !== "once",
|
|
1183
|
+
loopMode: loopMode2,
|
|
1184
|
+
repeatCount: state2?.repeatCount,
|
|
1185
|
+
isLooping: loopMode2 !== "once"
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
626
1188
|
const action = this.animationActions.get(clipName);
|
|
627
1189
|
if (!action) return null;
|
|
628
1190
|
const clip = action.getClip();
|
|
@@ -643,7 +1205,9 @@ var BakedAnimationController = class {
|
|
|
643
1205
|
reverse,
|
|
644
1206
|
weight: state?.weight ?? action.getEffectiveWeight(),
|
|
645
1207
|
balance: state?.balance ?? 0,
|
|
1208
|
+
requestedBlendMode: state?.requestedBlendMode ?? state?.blendMode ?? "replace",
|
|
646
1209
|
blendMode: state?.blendMode ?? "replace",
|
|
1210
|
+
channels: state?.source === "baked" ? this.getBakedChannelInfo(clipName, state) : void 0,
|
|
647
1211
|
easing: state?.easing ?? "linear",
|
|
648
1212
|
loop: loopMode !== "once",
|
|
649
1213
|
loopMode,
|
|
@@ -653,6 +1217,12 @@ var BakedAnimationController = class {
|
|
|
653
1217
|
}
|
|
654
1218
|
getPlayingAnimations() {
|
|
655
1219
|
const playing = [];
|
|
1220
|
+
for (const name of this.bakedActionGroups.keys()) {
|
|
1221
|
+
const state = this.getAnimationState(name);
|
|
1222
|
+
if (state?.isPlaying) {
|
|
1223
|
+
playing.push(state);
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
656
1226
|
for (const [name, action] of this.animationActions) {
|
|
657
1227
|
if (action.isRunning()) {
|
|
658
1228
|
const state = this.getAnimationState(name);
|
|
@@ -662,6 +1232,13 @@ var BakedAnimationController = class {
|
|
|
662
1232
|
return playing;
|
|
663
1233
|
}
|
|
664
1234
|
crossfadeTo(clipName, duration = 0.3, options = {}) {
|
|
1235
|
+
for (const group of this.bakedActionGroups.values()) {
|
|
1236
|
+
for (const action of group.channelActions.values()) {
|
|
1237
|
+
if (action.isRunning()) {
|
|
1238
|
+
action.fadeOut(duration);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
665
1242
|
for (const action of this.animationActions.values()) {
|
|
666
1243
|
if (action.isRunning()) {
|
|
667
1244
|
action.fadeOut(duration);
|
|
@@ -896,6 +1473,7 @@ var BakedAnimationController = class {
|
|
|
896
1473
|
return null;
|
|
897
1474
|
}
|
|
898
1475
|
const clip = new THREE.AnimationClip(clipName, maxTime, tracks);
|
|
1476
|
+
this.setClipEventMetadata(clip, { keyframeTimes });
|
|
899
1477
|
console.log(`[Loom3] snippetToClip: Created clip "${clipName}" with ${tracks.length} tracks, duration ${maxTime.toFixed(2)}s`);
|
|
900
1478
|
return clip;
|
|
901
1479
|
}
|
|
@@ -927,28 +1505,38 @@ var BakedAnimationController = class {
|
|
|
927
1505
|
this.animationClips.push(clip);
|
|
928
1506
|
}
|
|
929
1507
|
this.applyPlaybackState(action, playbackState);
|
|
1508
|
+
if (actionId) {
|
|
1509
|
+
this.cleanupClipMonitor(actionId);
|
|
1510
|
+
}
|
|
930
1511
|
let resolveFinished;
|
|
931
1512
|
const finishedPromise = new Promise((resolve) => {
|
|
932
1513
|
resolveFinished = resolve;
|
|
933
1514
|
});
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1515
|
+
const keyframeTimes = this.getClipEventMetadata(clip).keyframeTimes;
|
|
1516
|
+
const initialDirection = playbackState.reverse ? -1 : 1;
|
|
1517
|
+
const monitor = {
|
|
1518
|
+
action,
|
|
1519
|
+
actionId,
|
|
1520
|
+
clip,
|
|
1521
|
+
clipName: clip.name,
|
|
1522
|
+
duration: clip.duration,
|
|
1523
|
+
keyframeTimes,
|
|
1524
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
1525
|
+
initialDirection,
|
|
1526
|
+
direction: initialDirection,
|
|
1527
|
+
iteration: 0,
|
|
1528
|
+
lastTime: Math.max(0, Math.min(clip.duration, action.time)),
|
|
1529
|
+
lastKeyframeIndex: this.getKeyframeIndex(keyframeTimes, action.time),
|
|
1530
|
+
loopMode: playbackState.loopMode,
|
|
1531
|
+
finishedPending: false,
|
|
1532
|
+
cleanedUp: false,
|
|
1533
|
+
resolveFinished
|
|
943
1534
|
};
|
|
944
|
-
this.
|
|
945
|
-
resolveFinished();
|
|
946
|
-
cleanup();
|
|
947
|
-
});
|
|
948
|
-
finishedPromise.catch(() => cleanup());
|
|
1535
|
+
this.clipMonitors.set(actionId, monitor);
|
|
949
1536
|
action.reset();
|
|
950
1537
|
action.time = startTime;
|
|
951
1538
|
action.play();
|
|
1539
|
+
this.resetClipMonitor(monitor, action.time);
|
|
952
1540
|
this.clipActions.set(clip.name, action);
|
|
953
1541
|
this.animationActions.set(clip.name, action);
|
|
954
1542
|
this.setPlaybackState(clip.name, playbackState);
|
|
@@ -966,6 +1554,7 @@ var BakedAnimationController = class {
|
|
|
966
1554
|
})
|
|
967
1555
|
);
|
|
968
1556
|
action.play();
|
|
1557
|
+
this.resetClipMonitor(monitor, action.time);
|
|
969
1558
|
},
|
|
970
1559
|
stop: () => {
|
|
971
1560
|
action.stop();
|
|
@@ -983,8 +1572,7 @@ var BakedAnimationController = class {
|
|
|
983
1572
|
this.animationActions.delete(clip.name);
|
|
984
1573
|
this.animationFinishedCallbacks.delete(clip.name);
|
|
985
1574
|
this.playbackState.delete(clip.name);
|
|
986
|
-
|
|
987
|
-
cleanup();
|
|
1575
|
+
this.cleanupClipMonitor(actionId);
|
|
988
1576
|
},
|
|
989
1577
|
pause: () => {
|
|
990
1578
|
action.paused = true;
|
|
@@ -1002,6 +1590,8 @@ var BakedAnimationController = class {
|
|
|
1002
1590
|
const next = this.playbackState.get(clip.name) ?? playbackState;
|
|
1003
1591
|
next.playbackRate = Number.isFinite(r) ? Math.max(0, Math.abs(r)) : 1;
|
|
1004
1592
|
this.applyPlaybackState(action, next);
|
|
1593
|
+
monitor.direction = next.reverse ? -1 : 1;
|
|
1594
|
+
monitor.initialDirection = monitor.direction;
|
|
1005
1595
|
this.setPlaybackState(clip.name, next);
|
|
1006
1596
|
},
|
|
1007
1597
|
setLoop: (mode, repeatCount) => {
|
|
@@ -1010,6 +1600,7 @@ var BakedAnimationController = class {
|
|
|
1010
1600
|
next.loop = mode !== "once";
|
|
1011
1601
|
next.repeatCount = repeatCount;
|
|
1012
1602
|
this.applyPlaybackState(action, next);
|
|
1603
|
+
monitor.loopMode = mode;
|
|
1013
1604
|
this.setPlaybackState(clip.name, next);
|
|
1014
1605
|
},
|
|
1015
1606
|
setTime: (t) => {
|
|
@@ -1019,9 +1610,16 @@ var BakedAnimationController = class {
|
|
|
1019
1610
|
this.animationMixer?.update(0);
|
|
1020
1611
|
} catch {
|
|
1021
1612
|
}
|
|
1613
|
+
this.syncClipMonitorTime(monitor, clamped, true);
|
|
1022
1614
|
},
|
|
1023
1615
|
getTime: () => action.time,
|
|
1024
1616
|
getDuration: () => clip.duration,
|
|
1617
|
+
subscribe: (listener) => {
|
|
1618
|
+
monitor.listeners.add(listener);
|
|
1619
|
+
return () => {
|
|
1620
|
+
monitor.listeners.delete(listener);
|
|
1621
|
+
};
|
|
1622
|
+
},
|
|
1025
1623
|
finished: finishedPromise
|
|
1026
1624
|
};
|
|
1027
1625
|
this.clipHandles.set(clip.name, handle);
|
|
@@ -1045,6 +1643,7 @@ var BakedAnimationController = class {
|
|
|
1045
1643
|
if (!this.animationMixer || !this.host.getModel()) return;
|
|
1046
1644
|
for (const [clipName, action] of Array.from(this.clipActions.entries())) {
|
|
1047
1645
|
if (clipName === name || clipName.startsWith(`${name}_`)) {
|
|
1646
|
+
const actionId = this.getActionId(action);
|
|
1048
1647
|
try {
|
|
1049
1648
|
action.stop();
|
|
1050
1649
|
const clip = action.getClip();
|
|
@@ -1059,6 +1658,7 @@ var BakedAnimationController = class {
|
|
|
1059
1658
|
this.clipHandles.delete(clipName);
|
|
1060
1659
|
this.animationFinishedCallbacks.delete(clipName);
|
|
1061
1660
|
this.playbackState.delete(clipName);
|
|
1661
|
+
if (actionId) this.cleanupClipMonitor(actionId);
|
|
1062
1662
|
}
|
|
1063
1663
|
}
|
|
1064
1664
|
}
|
|
@@ -1082,6 +1682,8 @@ var BakedAnimationController = class {
|
|
|
1082
1682
|
console.log("[Loom3] updateClipParams start", debugSnapshot());
|
|
1083
1683
|
const apply = (action) => {
|
|
1084
1684
|
if (!action) return;
|
|
1685
|
+
const actionId = this.getActionId(action);
|
|
1686
|
+
const monitor = actionId ? this.clipMonitors.get(actionId) : void 0;
|
|
1085
1687
|
const clipName = action.getClip().name;
|
|
1086
1688
|
const next = this.playbackState.get(clipName) ?? this.normalizePlaybackOptions(void 0, { loop: false, source: this.clipSources.get(clipName) ?? "clip" });
|
|
1087
1689
|
try {
|
|
@@ -1100,6 +1702,10 @@ var BakedAnimationController = class {
|
|
|
1100
1702
|
}
|
|
1101
1703
|
const signedRate = next.reverse ? -next.playbackRate : next.playbackRate;
|
|
1102
1704
|
action.setEffectiveTimeScale(signedRate);
|
|
1705
|
+
if (monitor) {
|
|
1706
|
+
monitor.direction = next.reverse ? -1 : 1;
|
|
1707
|
+
monitor.initialDirection = monitor.direction;
|
|
1708
|
+
}
|
|
1103
1709
|
updated = true;
|
|
1104
1710
|
}
|
|
1105
1711
|
if (typeof params.loop === "boolean" || params.loopMode || params.repeatCount !== void 0) {
|
|
@@ -1107,6 +1713,7 @@ var BakedAnimationController = class {
|
|
|
1107
1713
|
next.loop = next.loopMode !== "once";
|
|
1108
1714
|
next.repeatCount = params.repeatCount;
|
|
1109
1715
|
this.applyPlaybackState(action, next);
|
|
1716
|
+
if (monitor) monitor.loopMode = next.loopMode;
|
|
1110
1717
|
updated = true;
|
|
1111
1718
|
}
|
|
1112
1719
|
this.setPlaybackState(clipName, next);
|
|
@@ -1186,7 +1793,23 @@ var BakedAnimationController = class {
|
|
|
1186
1793
|
if (this.animationMixer && !this.mixerFinishedListenerAttached) {
|
|
1187
1794
|
this.animationMixer.addEventListener("finished", (event) => {
|
|
1188
1795
|
const action = event.action;
|
|
1796
|
+
const actionId = this.getActionId(action);
|
|
1797
|
+
if (actionId) {
|
|
1798
|
+
const monitor = this.clipMonitors.get(actionId);
|
|
1799
|
+
if (monitor) {
|
|
1800
|
+
monitor.finishedPending = true;
|
|
1801
|
+
return;
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1189
1804
|
const clip = action.getClip();
|
|
1805
|
+
const bakedRuntime = this.bakedRuntimeClipToSource.get(clip.name);
|
|
1806
|
+
if (bakedRuntime) {
|
|
1807
|
+
const group = this.bakedActionGroups.get(bakedRuntime.sourceClipName);
|
|
1808
|
+
if (group && group.pendingFinishedChannels.delete(bakedRuntime.channel) && group.pendingFinishedChannels.size === 0) {
|
|
1809
|
+
group.resolveFinished();
|
|
1810
|
+
}
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1190
1813
|
const callback = this.animationFinishedCallbacks.get(clip.name);
|
|
1191
1814
|
if (callback) {
|
|
1192
1815
|
callback();
|
|
@@ -1211,6 +1834,20 @@ var BakedAnimationController = class {
|
|
|
1211
1834
|
finished: finishedPromise
|
|
1212
1835
|
};
|
|
1213
1836
|
}
|
|
1837
|
+
createBakedAnimationHandle(clipName, group) {
|
|
1838
|
+
return {
|
|
1839
|
+
actionId: group.actionId,
|
|
1840
|
+
stop: () => this.stopAnimation(clipName),
|
|
1841
|
+
pause: () => this.pauseAnimation(clipName),
|
|
1842
|
+
resume: () => this.resumeAnimation(clipName),
|
|
1843
|
+
setSpeed: (speed) => this.setAnimationSpeed(clipName, speed),
|
|
1844
|
+
setWeight: (weight) => this.setAnimationIntensity(clipName, weight),
|
|
1845
|
+
seekTo: (time) => this.seekAnimation(clipName, time),
|
|
1846
|
+
getState: () => this.getAnimationState(clipName),
|
|
1847
|
+
crossfadeTo: (targetClip, dur) => this.crossfadeTo(targetClip, dur),
|
|
1848
|
+
finished: group.finishedPromise
|
|
1849
|
+
};
|
|
1850
|
+
}
|
|
1214
1851
|
};
|
|
1215
1852
|
|
|
1216
1853
|
// src/presets/cc4.ts
|
|
@@ -3870,9 +4507,11 @@ function getPreset(presetType) {
|
|
|
3870
4507
|
return CC4_PRESET;
|
|
3871
4508
|
}
|
|
3872
4509
|
}
|
|
4510
|
+
var resolvePreset = getPreset;
|
|
3873
4511
|
function getPresetWithProfile(presetType, profile) {
|
|
3874
4512
|
return extendPresetWithProfile(getPreset(presetType), profile);
|
|
3875
4513
|
}
|
|
4514
|
+
var resolvePresetWithOverrides = getPresetWithProfile;
|
|
3876
4515
|
|
|
3877
4516
|
// src/engines/three/Loom3.ts
|
|
3878
4517
|
var deg2rad = (d) => d * Math.PI / 180;
|
|
@@ -7028,6 +7667,8 @@ exports.mergeCharacterRegionsByName = mergeRegionsByName;
|
|
|
7028
7667
|
exports.resolveBoneName = resolveBoneName;
|
|
7029
7668
|
exports.resolveBoneNames = resolveBoneNames;
|
|
7030
7669
|
exports.resolveFaceCenter = resolveFaceCenter;
|
|
7670
|
+
exports.resolvePreset = resolvePreset;
|
|
7671
|
+
exports.resolvePresetWithOverrides = resolvePresetWithOverrides;
|
|
7031
7672
|
exports.suggestBestPreset = suggestBestPreset;
|
|
7032
7673
|
exports.validateMappingConfig = validateMappingConfig;
|
|
7033
7674
|
exports.validateMappings = validateMappings;
|