@lovelace_lol/loom3 1.0.43 → 1.0.44
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/dist/index.cjs +91 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +91 -48
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -74,6 +74,9 @@ function bindingTargets(binding) {
|
|
|
74
74
|
if (targets && targets.length > 0) return targets;
|
|
75
75
|
return binding.morph !== void 0 && binding.morph !== "" ? [binding.morph] : [];
|
|
76
76
|
}
|
|
77
|
+
function normalizeBindingWeight(weight) {
|
|
78
|
+
return Number.isFinite(weight) ? Math.max(0, weight ?? 1) : 1;
|
|
79
|
+
}
|
|
77
80
|
function getProfileVisemeSlots(profile) {
|
|
78
81
|
if (profile.visemeSlots && profile.visemeSlots.length > 0) {
|
|
79
82
|
return [...profile.visemeSlots].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
@@ -99,6 +102,23 @@ function compileVisemeKeys(profile) {
|
|
|
99
102
|
return target ?? profile.visemeKeys?.[index] ?? "";
|
|
100
103
|
});
|
|
101
104
|
}
|
|
105
|
+
function getVisemeBindingTargets(profile, visemeIndex) {
|
|
106
|
+
const slots = getProfileVisemeSlots(profile);
|
|
107
|
+
const slot = slots[visemeIndex];
|
|
108
|
+
const binding = slot ? profile.visemeBindings?.[slot.id] : void 0;
|
|
109
|
+
const boundTargets = binding?.targets?.filter((target) => target.morph !== void 0 && target.morph !== "").map((target) => ({
|
|
110
|
+
morph: target.morph,
|
|
111
|
+
weight: normalizeBindingWeight(target.weight)
|
|
112
|
+
}));
|
|
113
|
+
if (boundTargets && boundTargets.length > 0) {
|
|
114
|
+
return boundTargets;
|
|
115
|
+
}
|
|
116
|
+
if (binding?.morph !== void 0 && binding.morph !== "") {
|
|
117
|
+
return [{ morph: binding.morph, weight: 1 }];
|
|
118
|
+
}
|
|
119
|
+
const legacyTarget = profile.visemeKeys?.[visemeIndex];
|
|
120
|
+
return legacyTarget !== void 0 && legacyTarget !== "" ? [{ morph: legacyTarget, weight: 1 }] : [];
|
|
121
|
+
}
|
|
102
122
|
function getVisemeJawAmounts(profile) {
|
|
103
123
|
const slots = getProfileVisemeSlots(profile);
|
|
104
124
|
if (slots.length === 0) return profile.visemeJawAmounts ? [...profile.visemeJawAmounts] : void 0;
|
|
@@ -1735,12 +1755,13 @@ var BakedAnimationController = class {
|
|
|
1735
1755
|
const globalBalance = options?.balance ?? 0;
|
|
1736
1756
|
const balanceMap = options?.balanceMap;
|
|
1737
1757
|
const meshNames = options?.meshNames;
|
|
1758
|
+
const visemeSlotCount = getProfileVisemeSlots(config).length;
|
|
1738
1759
|
let maxTime = 0;
|
|
1739
1760
|
const isNumericAU = (id) => /^\d+$/.test(id);
|
|
1740
1761
|
const isVisemeIndex = (id) => {
|
|
1741
1762
|
if (options?.snippetCategory !== "visemeSnippet") return false;
|
|
1742
1763
|
const num = Number(id);
|
|
1743
|
-
return !Number.isNaN(num) && num >= 0 && num <
|
|
1764
|
+
return !Number.isNaN(num) && num >= 0 && num < visemeSlotCount;
|
|
1744
1765
|
};
|
|
1745
1766
|
const sampleAt = (arr, t) => {
|
|
1746
1767
|
if (!arr.length) return 0;
|
|
@@ -1778,11 +1799,13 @@ var BakedAnimationController = class {
|
|
|
1778
1799
|
const auId = Number(curveId);
|
|
1779
1800
|
if (isVisemeIndex(curveId)) {
|
|
1780
1801
|
const visemeMeshNames = this.getMeshNamesForViseme(config, meshNames);
|
|
1781
|
-
const
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1802
|
+
for (const target of getVisemeBindingTargets(config, auId)) {
|
|
1803
|
+
const effectiveScale = intensityScale * target.weight;
|
|
1804
|
+
if (typeof target.morph === "number") {
|
|
1805
|
+
this.addMorphIndexTracks(tracks, target.morph, keyframes, effectiveScale, visemeMeshNames);
|
|
1806
|
+
} else if (target.morph) {
|
|
1807
|
+
this.addMorphTracks(tracks, target.morph, keyframes, effectiveScale, visemeMeshNames);
|
|
1808
|
+
}
|
|
1786
1809
|
}
|
|
1787
1810
|
} else {
|
|
1788
1811
|
const auMeshNames = this.getMeshNamesForAU(auId, config, meshNames);
|
|
@@ -1825,7 +1848,7 @@ var BakedAnimationController = class {
|
|
|
1825
1848
|
}
|
|
1826
1849
|
const autoVisemeJaw = options?.autoVisemeJaw !== false;
|
|
1827
1850
|
const jawScale = options?.jawScale ?? 1;
|
|
1828
|
-
const visemeJawAmounts = config
|
|
1851
|
+
const visemeJawAmounts = getVisemeJawAmounts(config);
|
|
1829
1852
|
if (autoVisemeJaw && jawScale > 0 && visemeJawAmounts && options?.snippetCategory === "visemeSnippet" && keyframeTimes.length > 0) {
|
|
1830
1853
|
const bones = this.host.getBones();
|
|
1831
1854
|
const jawEntry = bones["JAW"];
|
|
@@ -1833,7 +1856,7 @@ var BakedAnimationController = class {
|
|
|
1833
1856
|
const jawValues = [];
|
|
1834
1857
|
for (const t of keyframeTimes) {
|
|
1835
1858
|
let jawAmount = 0;
|
|
1836
|
-
for (let visemeIdx = 0; visemeIdx <
|
|
1859
|
+
for (let visemeIdx = 0; visemeIdx < visemeSlotCount; visemeIdx++) {
|
|
1837
1860
|
const visemeCurve = curves[String(visemeIdx)];
|
|
1838
1861
|
if (!visemeCurve) continue;
|
|
1839
1862
|
const visemeValue = clampIntensity(sampleAt(visemeCurve, t) * intensityScale);
|
|
@@ -5361,10 +5384,15 @@ var _Loom3 = class _Loom3 {
|
|
|
5361
5384
|
};
|
|
5362
5385
|
this.resolvedAUMorphTargets.set(auId, resolved);
|
|
5363
5386
|
}
|
|
5364
|
-
for (let i = 0; i < (this.config
|
|
5365
|
-
const key = this.config.visemeKeys[i];
|
|
5387
|
+
for (let i = 0; i < getProfileVisemeSlots(this.config).length; i += 1) {
|
|
5366
5388
|
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
5367
|
-
const targets =
|
|
5389
|
+
const targets = [];
|
|
5390
|
+
for (const bindingTarget of getVisemeBindingTargets(this.config, i)) {
|
|
5391
|
+
const resolved = typeof bindingTarget.morph === "number" ? this.resolveMorphTargetsByIndex(bindingTarget.morph, visemeMeshNames) : this.resolveMorphTargets(bindingTarget.morph, visemeMeshNames);
|
|
5392
|
+
for (const target of resolved) {
|
|
5393
|
+
targets.push({ ...target, weight: bindingTarget.weight });
|
|
5394
|
+
}
|
|
5395
|
+
}
|
|
5368
5396
|
this.resolvedVisemeTargets[i] = targets;
|
|
5369
5397
|
}
|
|
5370
5398
|
}
|
|
@@ -5832,46 +5860,33 @@ var _Loom3 = class _Loom3 {
|
|
|
5832
5860
|
// VISEME CONTROL
|
|
5833
5861
|
// ============================================================================
|
|
5834
5862
|
setViseme(visemeIndex, value, jawScale = 1) {
|
|
5835
|
-
if (visemeIndex < 0 || visemeIndex >= this.
|
|
5863
|
+
if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
|
|
5836
5864
|
const val = clamp012(value);
|
|
5837
5865
|
this.visemeValues[visemeIndex] = val;
|
|
5838
5866
|
this.visemeJawScales[visemeIndex] = jawScale;
|
|
5839
|
-
|
|
5840
|
-
if (targets && targets.length > 0) {
|
|
5841
|
-
this.applyMorphTargets(targets, val);
|
|
5842
|
-
} else {
|
|
5843
|
-
const morphKey = this.config.visemeKeys[visemeIndex];
|
|
5844
|
-
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
5845
|
-
if (typeof morphKey === "number") {
|
|
5846
|
-
this.setMorphInfluence(morphKey, val, visemeMeshNames);
|
|
5847
|
-
} else if (typeof morphKey === "string") {
|
|
5848
|
-
this.setMorph(morphKey, val, visemeMeshNames);
|
|
5849
|
-
}
|
|
5850
|
-
}
|
|
5851
|
-
const jawAmount = this.getVisemeJawAmount(visemeIndex) * val * jawScale;
|
|
5852
|
-
if (Math.abs(jawScale) > 1e-6 && Math.abs(jawAmount) > 1e-6) {
|
|
5853
|
-
this.updateBoneRotation("JAW", "pitch", jawAmount);
|
|
5854
|
-
}
|
|
5867
|
+
this.applyVisemeRuntimeState();
|
|
5855
5868
|
}
|
|
5856
5869
|
transitionViseme(visemeIndex, to, durationMs = 80, jawScale = 1) {
|
|
5857
|
-
if (visemeIndex < 0 || visemeIndex >= this.
|
|
5870
|
+
if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) {
|
|
5858
5871
|
return { promise: Promise.resolve(), pause: () => {
|
|
5859
5872
|
}, resume: () => {
|
|
5860
5873
|
}, cancel: () => {
|
|
5861
5874
|
} };
|
|
5862
5875
|
}
|
|
5863
|
-
const morphKey = this.config.visemeKeys[visemeIndex];
|
|
5864
5876
|
const target = clamp012(to);
|
|
5865
|
-
this.visemeValues[visemeIndex]
|
|
5877
|
+
const from = this.visemeValues[visemeIndex] ?? 0;
|
|
5866
5878
|
this.visemeJawScales[visemeIndex] = jawScale;
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5879
|
+
return this.animation.addTransition(
|
|
5880
|
+
`viseme_value_${visemeIndex}`,
|
|
5881
|
+
from,
|
|
5882
|
+
target,
|
|
5883
|
+
durationMs,
|
|
5884
|
+
(value) => {
|
|
5885
|
+
this.visemeValues[visemeIndex] = clamp012(value);
|
|
5886
|
+
this.visemeJawScales[visemeIndex] = jawScale;
|
|
5887
|
+
this.applyVisemeRuntimeState();
|
|
5888
|
+
}
|
|
5889
|
+
);
|
|
5875
5890
|
}
|
|
5876
5891
|
setVisemeById(slotId, value, jawScale = 1) {
|
|
5877
5892
|
const index = getVisemeSlotIndex(this.config, slotId);
|
|
@@ -5935,8 +5950,9 @@ var _Loom3 = class _Loom3 {
|
|
|
5935
5950
|
}
|
|
5936
5951
|
resetToNeutral() {
|
|
5937
5952
|
this.auValues = {};
|
|
5938
|
-
|
|
5939
|
-
this.
|
|
5953
|
+
const visemeCount = getProfileVisemeSlots(this.config).length;
|
|
5954
|
+
this.visemeValues = new Array(visemeCount).fill(0);
|
|
5955
|
+
this.visemeJawScales = new Array(visemeCount).fill(1);
|
|
5940
5956
|
this.translations = {};
|
|
5941
5957
|
this.initBoneRotations();
|
|
5942
5958
|
this.clearTransitions();
|
|
@@ -5970,11 +5986,7 @@ var _Loom3 = class _Loom3 {
|
|
|
5970
5986
|
if (Number.isNaN(auId)) continue;
|
|
5971
5987
|
this.setAU(auId, value, this.auBalances[auId]);
|
|
5972
5988
|
}
|
|
5973
|
-
|
|
5974
|
-
const value = this.visemeValues[visemeIndex] ?? 0;
|
|
5975
|
-
if (value <= 0) continue;
|
|
5976
|
-
this.setViseme(visemeIndex, value, this.visemeJawScales[visemeIndex] ?? 1);
|
|
5977
|
-
}
|
|
5989
|
+
this.applyVisemeRuntimeState();
|
|
5978
5990
|
if (this.model) {
|
|
5979
5991
|
this.flushPendingComposites();
|
|
5980
5992
|
this.model.updateMatrixWorld(true);
|
|
@@ -6332,6 +6344,37 @@ var _Loom3 = class _Loom3 {
|
|
|
6332
6344
|
target.infl[target.idx] = val;
|
|
6333
6345
|
}
|
|
6334
6346
|
}
|
|
6347
|
+
applyVisemeRuntimeState() {
|
|
6348
|
+
for (const targets of this.resolvedVisemeTargets) {
|
|
6349
|
+
for (const target of targets || []) {
|
|
6350
|
+
if (target.idx < target.infl.length) {
|
|
6351
|
+
target.infl[target.idx] = 0;
|
|
6352
|
+
}
|
|
6353
|
+
}
|
|
6354
|
+
}
|
|
6355
|
+
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
6356
|
+
const value = clamp012(this.visemeValues[index] ?? 0);
|
|
6357
|
+
if (value <= 1e-6) continue;
|
|
6358
|
+
const targets = this.resolvedVisemeTargets[index] || [];
|
|
6359
|
+
for (const target of targets) {
|
|
6360
|
+
if (target.idx >= target.infl.length) continue;
|
|
6361
|
+
const weighted = clamp012(value * target.weight);
|
|
6362
|
+
target.infl[target.idx] = Math.max(target.infl[target.idx] ?? 0, weighted);
|
|
6363
|
+
}
|
|
6364
|
+
}
|
|
6365
|
+
this.updateBoneRotation("JAW", "pitch", this.getActiveVisemeJawAmount());
|
|
6366
|
+
}
|
|
6367
|
+
getActiveVisemeJawAmount() {
|
|
6368
|
+
let jawAmount = 0;
|
|
6369
|
+
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
6370
|
+
const value = clamp012(this.visemeValues[index] ?? 0);
|
|
6371
|
+
if (value <= 1e-6) continue;
|
|
6372
|
+
const jawScale = this.visemeJawScales[index] ?? 1;
|
|
6373
|
+
if (Math.abs(jawScale) <= 1e-6) continue;
|
|
6374
|
+
jawAmount = Math.max(jawAmount, this.getVisemeJawAmount(index) * value * jawScale);
|
|
6375
|
+
}
|
|
6376
|
+
return jawAmount;
|
|
6377
|
+
}
|
|
6335
6378
|
getMorphValue(key) {
|
|
6336
6379
|
if (this.faceMesh) {
|
|
6337
6380
|
const dict = this.faceMesh.morphTargetDictionary;
|
|
@@ -6517,7 +6560,7 @@ var _Loom3 = class _Loom3 {
|
|
|
6517
6560
|
return meshNames?.length ? `idx:${index}@${meshNames.join(",")}` : `idx:${index}`;
|
|
6518
6561
|
}
|
|
6519
6562
|
syncVisemeRuntimeState() {
|
|
6520
|
-
const visemeCount = this.config.
|
|
6563
|
+
const visemeCount = getProfileVisemeSlots(this.config).length;
|
|
6521
6564
|
this.visemeValues = Array.from(
|
|
6522
6565
|
{ length: visemeCount },
|
|
6523
6566
|
(_, index) => this.visemeValues[index] ?? 0
|
|
@@ -8640,6 +8683,7 @@ exports.getModelForwardDirection = getModelForwardDirection;
|
|
|
8640
8683
|
exports.getPreset = getPreset;
|
|
8641
8684
|
exports.getPresetWithProfile = getPresetWithProfile;
|
|
8642
8685
|
exports.getProfileVisemeSlots = getProfileVisemeSlots;
|
|
8686
|
+
exports.getVisemeBindingTargets = getVisemeBindingTargets;
|
|
8643
8687
|
exports.getVisemeJawAmounts = getVisemeJawAmounts;
|
|
8644
8688
|
exports.getVisemeSlotIndex = getVisemeSlotIndex;
|
|
8645
8689
|
exports.hasLeftRightMorphs = hasLeftRightMorphs;
|