@lovelace_lol/loom3 1.0.1 → 1.0.3
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 +1 -1
- package/dist/index.cjs +44 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +44 -34
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -519,6 +519,12 @@ interface ClipOptions {
|
|
|
519
519
|
mixerWeight?: number;
|
|
520
520
|
/** Left/right balance for bilateral AUs (-1 to 1, default: 0) */
|
|
521
521
|
balance?: number;
|
|
522
|
+
/**
|
|
523
|
+
* Per-curve left/right balance overrides keyed by curve id (typically AU ids as strings).
|
|
524
|
+
* Example: { "43": 1, "12": 0.7 }.
|
|
525
|
+
* Falls back to `balance` when a curve id is not present.
|
|
526
|
+
*/
|
|
527
|
+
balanceMap?: Record<string, number>;
|
|
522
528
|
/** Jaw scale for viseme playback (default: 1.0) */
|
|
523
529
|
jawScale?: number;
|
|
524
530
|
/** Intensity scale multiplier (default: 1.0) */
|
package/dist/index.d.ts
CHANGED
|
@@ -519,6 +519,12 @@ interface ClipOptions {
|
|
|
519
519
|
mixerWeight?: number;
|
|
520
520
|
/** Left/right balance for bilateral AUs (-1 to 1, default: 0) */
|
|
521
521
|
balance?: number;
|
|
522
|
+
/**
|
|
523
|
+
* Per-curve left/right balance overrides keyed by curve id (typically AU ids as strings).
|
|
524
|
+
* Example: { "43": 1, "12": 0.7 }.
|
|
525
|
+
* Falls back to `balance` when a curve id is not present.
|
|
526
|
+
*/
|
|
527
|
+
balanceMap?: Record<string, number>;
|
|
522
528
|
/** Jaw scale for viseme playback (default: 1.0) */
|
|
523
529
|
jawScale?: number;
|
|
524
530
|
/** Intensity scale multiplier (default: 1.0) */
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,20 @@ import { Vector3, Clock, Box3, Quaternion, LoopOnce, LoopPingPong, LoopRepeat, Q
|
|
|
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;
|
|
6
6
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
|
+
|
|
8
|
+
// src/engines/three/balanceUtils.ts
|
|
9
|
+
function clampBalance(value) {
|
|
10
|
+
if (!Number.isFinite(value)) return 0;
|
|
11
|
+
return Math.max(-1, Math.min(1, value));
|
|
12
|
+
}
|
|
13
|
+
function resolveCurveBalance(curveId, globalBalance, balanceMap) {
|
|
14
|
+
if (balanceMap && Object.prototype.hasOwnProperty.call(balanceMap, curveId)) {
|
|
15
|
+
return clampBalance(Number(balanceMap[curveId]));
|
|
16
|
+
}
|
|
17
|
+
return clampBalance(globalBalance);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/engines/three/AnimationThree.ts
|
|
7
21
|
var easeInOutQuad = (t) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
8
22
|
var AnimationThree = class {
|
|
9
23
|
constructor() {
|
|
@@ -115,6 +129,18 @@ var BakedAnimationController = class {
|
|
|
115
129
|
__publicField(this, "actionIdToClip", /* @__PURE__ */ new Map());
|
|
116
130
|
this.host = host;
|
|
117
131
|
}
|
|
132
|
+
getMeshNamesForAU(auId, config, explicitMeshNames) {
|
|
133
|
+
if (explicitMeshNames && explicitMeshNames.length > 0) {
|
|
134
|
+
return explicitMeshNames;
|
|
135
|
+
}
|
|
136
|
+
if (typeof this.host.getMeshNamesForAU === "function") {
|
|
137
|
+
return this.host.getMeshNamesForAU(auId) || [];
|
|
138
|
+
}
|
|
139
|
+
const facePart = config.auInfo?.[String(auId)]?.facePart;
|
|
140
|
+
if (facePart === "Tongue") return config.morphToMesh?.tongue || [];
|
|
141
|
+
if (facePart === "Eye") return config.morphToMesh?.eye || [];
|
|
142
|
+
return config.morphToMesh?.face || [];
|
|
143
|
+
}
|
|
118
144
|
update(dtSeconds) {
|
|
119
145
|
if (this.animationMixer) {
|
|
120
146
|
this.animationMixer.update(dtSeconds);
|
|
@@ -363,7 +389,8 @@ var BakedAnimationController = class {
|
|
|
363
389
|
}
|
|
364
390
|
const tracks = [];
|
|
365
391
|
const intensityScale = options?.intensityScale ?? 1;
|
|
366
|
-
const
|
|
392
|
+
const globalBalance = options?.balance ?? 0;
|
|
393
|
+
const balanceMap = options?.balanceMap;
|
|
367
394
|
const meshNames = options?.meshNames;
|
|
368
395
|
let maxTime = 0;
|
|
369
396
|
const isNumericAU = (id) => /^\d+$/.test(id);
|
|
@@ -414,35 +441,37 @@ var BakedAnimationController = class {
|
|
|
414
441
|
this.addMorphTracks(tracks, visemeKey, keyframes, intensityScale, meshNames);
|
|
415
442
|
}
|
|
416
443
|
} else {
|
|
444
|
+
const auMeshNames = this.getMeshNamesForAU(auId, config, meshNames);
|
|
417
445
|
const morphsBySide = config.auToMorphs[auId];
|
|
418
446
|
const mixWeight = this.host.isMixedAU(auId) ? this.host.getAUMixWeight(auId) : 1;
|
|
419
447
|
const leftKeys = morphsBySide?.left ?? [];
|
|
420
448
|
const rightKeys = morphsBySide?.right ?? [];
|
|
421
449
|
const centerKeys = morphsBySide?.center ?? [];
|
|
450
|
+
const curveBalance = resolveCurveBalance(curveId, globalBalance, balanceMap);
|
|
422
451
|
for (const morphKey of leftKeys) {
|
|
423
452
|
let effectiveScale = intensityScale * mixWeight;
|
|
424
|
-
if (
|
|
453
|
+
if (curveBalance > 0) effectiveScale *= 1 - curveBalance;
|
|
425
454
|
if (typeof morphKey === "number") {
|
|
426
|
-
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
455
|
+
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
427
456
|
} else {
|
|
428
|
-
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
457
|
+
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
429
458
|
}
|
|
430
459
|
}
|
|
431
460
|
for (const morphKey of rightKeys) {
|
|
432
461
|
let effectiveScale = intensityScale * mixWeight;
|
|
433
|
-
if (
|
|
462
|
+
if (curveBalance < 0) effectiveScale *= 1 + curveBalance;
|
|
434
463
|
if (typeof morphKey === "number") {
|
|
435
|
-
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
464
|
+
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
436
465
|
} else {
|
|
437
|
-
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
466
|
+
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
438
467
|
}
|
|
439
468
|
}
|
|
440
469
|
for (const morphKey of centerKeys) {
|
|
441
470
|
const effectiveScale = intensityScale * mixWeight;
|
|
442
471
|
if (typeof morphKey === "number") {
|
|
443
|
-
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
472
|
+
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
444
473
|
} else {
|
|
445
|
-
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
474
|
+
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
446
475
|
}
|
|
447
476
|
}
|
|
448
477
|
}
|
|
@@ -830,11 +859,9 @@ var BakedAnimationController = class {
|
|
|
830
859
|
}
|
|
831
860
|
addMorphTracks(tracks, morphKey, keyframes, intensityScale, meshNames) {
|
|
832
861
|
const config = this.host.getConfig();
|
|
833
|
-
const meshes = this.host.getMeshes();
|
|
834
862
|
const hasExplicitMeshes = !!(meshNames && meshNames.length > 0);
|
|
835
863
|
const targetMeshNames = hasExplicitMeshes ? meshNames : config.morphToMesh?.face || [];
|
|
836
|
-
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) :
|
|
837
|
-
let added = false;
|
|
864
|
+
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) : [];
|
|
838
865
|
const addTrackForMesh = (mesh) => {
|
|
839
866
|
const dict = mesh.morphTargetDictionary;
|
|
840
867
|
if (!dict || dict[morphKey] === void 0) return;
|
|
@@ -848,25 +875,17 @@ var BakedAnimationController = class {
|
|
|
848
875
|
const trackName = `${mesh.uuid}.morphTargetInfluences[${morphIndex}]`;
|
|
849
876
|
const track = new NumberKeyframeTrack(trackName, times, values);
|
|
850
877
|
tracks.push(track);
|
|
851
|
-
added = true;
|
|
852
878
|
};
|
|
853
879
|
for (const mesh of targetMeshes) {
|
|
854
880
|
addTrackForMesh(mesh);
|
|
855
881
|
}
|
|
856
|
-
if (!added && !hasExplicitMeshes && targetMeshes !== meshes) {
|
|
857
|
-
for (const mesh of meshes) {
|
|
858
|
-
addTrackForMesh(mesh);
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
882
|
}
|
|
862
883
|
addMorphIndexTracks(tracks, morphIndex, keyframes, intensityScale, meshNames) {
|
|
863
884
|
if (!Number.isInteger(morphIndex) || morphIndex < 0) return;
|
|
864
885
|
const config = this.host.getConfig();
|
|
865
|
-
const meshes = this.host.getMeshes();
|
|
866
886
|
const hasExplicitMeshes = !!(meshNames && meshNames.length > 0);
|
|
867
887
|
const targetMeshNames = hasExplicitMeshes ? meshNames : config.morphToMesh?.face || [];
|
|
868
|
-
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) :
|
|
869
|
-
let added = false;
|
|
888
|
+
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) : [];
|
|
870
889
|
const addTrackForMesh = (mesh) => {
|
|
871
890
|
const infl = mesh.morphTargetInfluences;
|
|
872
891
|
if (!infl || morphIndex < 0 || morphIndex >= infl.length) return;
|
|
@@ -879,16 +898,10 @@ var BakedAnimationController = class {
|
|
|
879
898
|
const trackName = `${mesh.uuid}.morphTargetInfluences[${morphIndex}]`;
|
|
880
899
|
const track = new NumberKeyframeTrack(trackName, times, values);
|
|
881
900
|
tracks.push(track);
|
|
882
|
-
added = true;
|
|
883
901
|
};
|
|
884
902
|
for (const mesh of targetMeshes) {
|
|
885
903
|
addTrackForMesh(mesh);
|
|
886
904
|
}
|
|
887
|
-
if (!added && !hasExplicitMeshes && targetMeshes !== meshes) {
|
|
888
|
-
for (const mesh of meshes) {
|
|
889
|
-
addTrackForMesh(mesh);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
905
|
}
|
|
893
906
|
ensureMixer() {
|
|
894
907
|
const model = this.host.getModel();
|
|
@@ -3492,6 +3505,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3492
3505
|
getModel: () => this.model,
|
|
3493
3506
|
getMeshes: () => this.meshes,
|
|
3494
3507
|
getMeshByName: (name) => this.meshByName.get(name),
|
|
3508
|
+
getMeshNamesForAU: (auId) => this.getMeshNamesForAU(auId),
|
|
3495
3509
|
getBones: () => this.bones,
|
|
3496
3510
|
getConfig: () => this.config,
|
|
3497
3511
|
getCompositeRotations: () => this.compositeRotations,
|
|
@@ -3537,15 +3551,11 @@ var _Loom3 = class _Loom3 {
|
|
|
3537
3551
|
this.resolvedFaceMeshes = this.resolveFaceMeshes(this.meshes);
|
|
3538
3552
|
this.faceMesh = this.resolvedFaceMeshes.length > 0 ? this.meshByName.get(this.resolvedFaceMeshes[0]) || null : null;
|
|
3539
3553
|
if (!this.config.morphToMesh?.face || this.config.morphToMesh.face.length === 0) {
|
|
3540
|
-
const
|
|
3541
|
-
|
|
3542
|
-
const dict = m.morphTargetDictionary;
|
|
3543
|
-
return Array.isArray(infl) && infl.length > 0 || dict && Object.keys(dict).length > 0;
|
|
3544
|
-
}).map((m) => m.name).filter(Boolean);
|
|
3545
|
-
if (morphMeshNames.length > 0) {
|
|
3554
|
+
const faceMeshNames = this.resolvedFaceMeshes.filter((name) => this.meshByName.has(name));
|
|
3555
|
+
if (faceMeshNames.length > 0) {
|
|
3546
3556
|
this.config.morphToMesh = {
|
|
3547
3557
|
...this.config.morphToMesh,
|
|
3548
|
-
face: Array.from(new Set(
|
|
3558
|
+
face: Array.from(new Set(faceMeshNames))
|
|
3549
3559
|
};
|
|
3550
3560
|
}
|
|
3551
3561
|
}
|