@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Loom3
2
2
 
3
- The missing character controller for Three.js, allowing 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.
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, meshNames);
476
+ this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
464
477
  } else {
465
- this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, meshNames);
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, meshNames);
485
+ this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
473
486
  } else {
474
- this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, meshNames);
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, meshNames);
493
+ this.addMorphIndexTracks(tracks, morphKey, keyframes, effectiveScale, auMeshNames);
481
494
  } else {
482
- this.addMorphTracks(tracks, morphKey, keyframes, effectiveScale, meshNames);
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) : meshes;
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) : meshes;
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 morphMeshNames = this.meshes.filter((m) => {
3578
- const infl = m.morphTargetInfluences;
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(morphMeshNames))
3579
+ face: Array.from(new Set(faceMeshNames))
3586
3580
  };
3587
3581
  }
3588
3582
  }