@lovelace_lol/loom3 1.0.2 → 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 +25 -31
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -31
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Loom3
|
|
2
2
|
|
|
3
|
-
The missing character controller for Three.js
|
|
3
|
+
The missing character controller for Three.js! Loom3 allows you to bring humanoid and animal characters to life. Loom3 is based on the Facial Action Coding System (FACS) as the basis of its mappings, providing a morph and bone mapping library for controlling high-definition 3D characters in Three.js.
|
|
4
4
|
|
|
5
5
|
Loom3 provides mappings that connect [Facial Action Coding System (FACS)](https://en.wikipedia.org/wiki/Facial_Action_Coding_System) Action Units to the morph targets and bone transforms found in Character Creator 4 (CC4) characters. Instead of manually figuring out which blend shapes correspond to which facial movements, you can simply say `setAU(12, 0.8)` and the library handles the rest.
|
|
6
6
|
|
package/dist/index.cjs
CHANGED
|
@@ -150,6 +150,18 @@ var BakedAnimationController = class {
|
|
|
150
150
|
__publicField(this, "actionIdToClip", /* @__PURE__ */ new Map());
|
|
151
151
|
this.host = host;
|
|
152
152
|
}
|
|
153
|
+
getMeshNamesForAU(auId, config, explicitMeshNames) {
|
|
154
|
+
if (explicitMeshNames && explicitMeshNames.length > 0) {
|
|
155
|
+
return explicitMeshNames;
|
|
156
|
+
}
|
|
157
|
+
if (typeof this.host.getMeshNamesForAU === "function") {
|
|
158
|
+
return this.host.getMeshNamesForAU(auId) || [];
|
|
159
|
+
}
|
|
160
|
+
const facePart = config.auInfo?.[String(auId)]?.facePart;
|
|
161
|
+
if (facePart === "Tongue") return config.morphToMesh?.tongue || [];
|
|
162
|
+
if (facePart === "Eye") return config.morphToMesh?.eye || [];
|
|
163
|
+
return config.morphToMesh?.face || [];
|
|
164
|
+
}
|
|
153
165
|
update(dtSeconds) {
|
|
154
166
|
if (this.animationMixer) {
|
|
155
167
|
this.animationMixer.update(dtSeconds);
|
|
@@ -450,6 +462,7 @@ var BakedAnimationController = class {
|
|
|
450
462
|
this.addMorphTracks(tracks, visemeKey, keyframes, intensityScale, meshNames);
|
|
451
463
|
}
|
|
452
464
|
} else {
|
|
465
|
+
const auMeshNames = this.getMeshNamesForAU(auId, config, meshNames);
|
|
453
466
|
const morphsBySide = config.auToMorphs[auId];
|
|
454
467
|
const mixWeight = this.host.isMixedAU(auId) ? this.host.getAUMixWeight(auId) : 1;
|
|
455
468
|
const leftKeys = morphsBySide?.left ?? [];
|
|
@@ -460,26 +473,26 @@ var BakedAnimationController = class {
|
|
|
460
473
|
let effectiveScale = intensityScale * mixWeight;
|
|
461
474
|
if (curveBalance > 0) effectiveScale *= 1 - curveBalance;
|
|
462
475
|
if (typeof morphKey === "number") {
|
|
463
|
-
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
476
|
+
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
464
477
|
} else {
|
|
465
|
-
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
478
|
+
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
466
479
|
}
|
|
467
480
|
}
|
|
468
481
|
for (const morphKey of rightKeys) {
|
|
469
482
|
let effectiveScale = intensityScale * mixWeight;
|
|
470
483
|
if (curveBalance < 0) effectiveScale *= 1 + curveBalance;
|
|
471
484
|
if (typeof morphKey === "number") {
|
|
472
|
-
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
485
|
+
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
473
486
|
} else {
|
|
474
|
-
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
487
|
+
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
475
488
|
}
|
|
476
489
|
}
|
|
477
490
|
for (const morphKey of centerKeys) {
|
|
478
491
|
const effectiveScale = intensityScale * mixWeight;
|
|
479
492
|
if (typeof morphKey === "number") {
|
|
480
|
-
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
493
|
+
this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
481
494
|
} else {
|
|
482
|
-
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale,
|
|
495
|
+
this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
|
|
483
496
|
}
|
|
484
497
|
}
|
|
485
498
|
}
|
|
@@ -867,11 +880,9 @@ var BakedAnimationController = class {
|
|
|
867
880
|
}
|
|
868
881
|
addMorphTracks(tracks, morphKey, keyframes, intensityScale, meshNames) {
|
|
869
882
|
const config = this.host.getConfig();
|
|
870
|
-
const meshes = this.host.getMeshes();
|
|
871
883
|
const hasExplicitMeshes = !!(meshNames && meshNames.length > 0);
|
|
872
884
|
const targetMeshNames = hasExplicitMeshes ? meshNames : config.morphToMesh?.face || [];
|
|
873
|
-
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) :
|
|
874
|
-
let added = false;
|
|
885
|
+
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) : [];
|
|
875
886
|
const addTrackForMesh = (mesh) => {
|
|
876
887
|
const dict = mesh.morphTargetDictionary;
|
|
877
888
|
if (!dict || dict[morphKey] === void 0) return;
|
|
@@ -885,25 +896,17 @@ var BakedAnimationController = class {
|
|
|
885
896
|
const trackName = `${mesh.uuid}.morphTargetInfluences[${morphIndex}]`;
|
|
886
897
|
const track = new THREE.NumberKeyframeTrack(trackName, times, values);
|
|
887
898
|
tracks.push(track);
|
|
888
|
-
added = true;
|
|
889
899
|
};
|
|
890
900
|
for (const mesh of targetMeshes) {
|
|
891
901
|
addTrackForMesh(mesh);
|
|
892
902
|
}
|
|
893
|
-
if (!added && !hasExplicitMeshes && targetMeshes !== meshes) {
|
|
894
|
-
for (const mesh of meshes) {
|
|
895
|
-
addTrackForMesh(mesh);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
903
|
}
|
|
899
904
|
addMorphIndexTracks(tracks, morphIndex, keyframes, intensityScale, meshNames) {
|
|
900
905
|
if (!Number.isInteger(morphIndex) || morphIndex < 0) return;
|
|
901
906
|
const config = this.host.getConfig();
|
|
902
|
-
const meshes = this.host.getMeshes();
|
|
903
907
|
const hasExplicitMeshes = !!(meshNames && meshNames.length > 0);
|
|
904
908
|
const targetMeshNames = hasExplicitMeshes ? meshNames : config.morphToMesh?.face || [];
|
|
905
|
-
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) :
|
|
906
|
-
let added = false;
|
|
909
|
+
const targetMeshes = targetMeshNames.length ? targetMeshNames.map((name) => this.host.getMeshByName(name)).filter(Boolean) : [];
|
|
907
910
|
const addTrackForMesh = (mesh) => {
|
|
908
911
|
const infl = mesh.morphTargetInfluences;
|
|
909
912
|
if (!infl || morphIndex < 0 || morphIndex >= infl.length) return;
|
|
@@ -916,16 +919,10 @@ var BakedAnimationController = class {
|
|
|
916
919
|
const trackName = `${mesh.uuid}.morphTargetInfluences[${morphIndex}]`;
|
|
917
920
|
const track = new THREE.NumberKeyframeTrack(trackName, times, values);
|
|
918
921
|
tracks.push(track);
|
|
919
|
-
added = true;
|
|
920
922
|
};
|
|
921
923
|
for (const mesh of targetMeshes) {
|
|
922
924
|
addTrackForMesh(mesh);
|
|
923
925
|
}
|
|
924
|
-
if (!added && !hasExplicitMeshes && targetMeshes !== meshes) {
|
|
925
|
-
for (const mesh of meshes) {
|
|
926
|
-
addTrackForMesh(mesh);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
926
|
}
|
|
930
927
|
ensureMixer() {
|
|
931
928
|
const model = this.host.getModel();
|
|
@@ -3529,6 +3526,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3529
3526
|
getModel: () => this.model,
|
|
3530
3527
|
getMeshes: () => this.meshes,
|
|
3531
3528
|
getMeshByName: (name) => this.meshByName.get(name),
|
|
3529
|
+
getMeshNamesForAU: (auId) => this.getMeshNamesForAU(auId),
|
|
3532
3530
|
getBones: () => this.bones,
|
|
3533
3531
|
getConfig: () => this.config,
|
|
3534
3532
|
getCompositeRotations: () => this.compositeRotations,
|
|
@@ -3574,15 +3572,11 @@ var _Loom3 = class _Loom3 {
|
|
|
3574
3572
|
this.resolvedFaceMeshes = this.resolveFaceMeshes(this.meshes);
|
|
3575
3573
|
this.faceMesh = this.resolvedFaceMeshes.length > 0 ? this.meshByName.get(this.resolvedFaceMeshes[0]) || null : null;
|
|
3576
3574
|
if (!this.config.morphToMesh?.face || this.config.morphToMesh.face.length === 0) {
|
|
3577
|
-
const
|
|
3578
|
-
|
|
3579
|
-
const dict = m.morphTargetDictionary;
|
|
3580
|
-
return Array.isArray(infl) && infl.length > 0 || dict && Object.keys(dict).length > 0;
|
|
3581
|
-
}).map((m) => m.name).filter(Boolean);
|
|
3582
|
-
if (morphMeshNames.length > 0) {
|
|
3575
|
+
const faceMeshNames = this.resolvedFaceMeshes.filter((name) => this.meshByName.has(name));
|
|
3576
|
+
if (faceMeshNames.length > 0) {
|
|
3583
3577
|
this.config.morphToMesh = {
|
|
3584
3578
|
...this.config.morphToMesh,
|
|
3585
|
-
face: Array.from(new Set(
|
|
3579
|
+
face: Array.from(new Set(faceMeshNames))
|
|
3586
3580
|
};
|
|
3587
3581
|
}
|
|
3588
3582
|
}
|