@lovelace_lol/loom3 1.0.34 → 1.0.35
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 +94 -6
- 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 +94 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1360,6 +1360,7 @@ declare class Loom3 implements LoomLarge {
|
|
|
1360
1360
|
private bones;
|
|
1361
1361
|
private mixWeights;
|
|
1362
1362
|
private visemeValues;
|
|
1363
|
+
private visemeJawScales;
|
|
1363
1364
|
private static readonly VISEME_JAW_AMOUNTS;
|
|
1364
1365
|
private bakedAnimations;
|
|
1365
1366
|
private hairPhysics;
|
|
@@ -1453,6 +1454,7 @@ declare class Loom3 implements LoomLarge {
|
|
|
1453
1454
|
clearTransitions(): void;
|
|
1454
1455
|
getActiveTransitionCount(): number;
|
|
1455
1456
|
resetToNeutral(): void;
|
|
1457
|
+
private reinitializeRuntimeStateFromCurrentControls;
|
|
1456
1458
|
getMeshList(): MeshInfo[];
|
|
1457
1459
|
/** Get all morph targets grouped by mesh name */
|
|
1458
1460
|
getMorphTargets(): Record<string, string[]>;
|
|
@@ -1556,6 +1558,10 @@ declare class Loom3 implements LoomLarge {
|
|
|
1556
1558
|
private getMorphValueByIndex;
|
|
1557
1559
|
private getMorphKeyCacheKey;
|
|
1558
1560
|
private getMorphIndexCacheKey;
|
|
1561
|
+
private syncVisemeRuntimeState;
|
|
1562
|
+
private getVisemeJawAmount;
|
|
1563
|
+
private collectResolvedExpressionMorphTargets;
|
|
1564
|
+
private resetMorphTargetHandles;
|
|
1559
1565
|
private isMixedAU;
|
|
1560
1566
|
private getEffectiveBoneAUValue;
|
|
1561
1567
|
private getCompositeAxisValueForNode;
|
package/dist/index.d.ts
CHANGED
|
@@ -1360,6 +1360,7 @@ declare class Loom3 implements LoomLarge {
|
|
|
1360
1360
|
private bones;
|
|
1361
1361
|
private mixWeights;
|
|
1362
1362
|
private visemeValues;
|
|
1363
|
+
private visemeJawScales;
|
|
1363
1364
|
private static readonly VISEME_JAW_AMOUNTS;
|
|
1364
1365
|
private bakedAnimations;
|
|
1365
1366
|
private hairPhysics;
|
|
@@ -1453,6 +1454,7 @@ declare class Loom3 implements LoomLarge {
|
|
|
1453
1454
|
clearTransitions(): void;
|
|
1454
1455
|
getActiveTransitionCount(): number;
|
|
1455
1456
|
resetToNeutral(): void;
|
|
1457
|
+
private reinitializeRuntimeStateFromCurrentControls;
|
|
1456
1458
|
getMeshList(): MeshInfo[];
|
|
1457
1459
|
/** Get all morph targets grouped by mesh name */
|
|
1458
1460
|
getMorphTargets(): Record<string, string[]>;
|
|
@@ -1556,6 +1558,10 @@ declare class Loom3 implements LoomLarge {
|
|
|
1556
1558
|
private getMorphValueByIndex;
|
|
1557
1559
|
private getMorphKeyCacheKey;
|
|
1558
1560
|
private getMorphIndexCacheKey;
|
|
1561
|
+
private syncVisemeRuntimeState;
|
|
1562
|
+
private getVisemeJawAmount;
|
|
1563
|
+
private collectResolvedExpressionMorphTargets;
|
|
1564
|
+
private resetMorphTargetHandles;
|
|
1559
1565
|
private isMixedAU;
|
|
1560
1566
|
private getEffectiveBoneAUValue;
|
|
1561
1567
|
private getCompositeAxisValueForNode;
|
package/dist/index.js
CHANGED
|
@@ -3916,7 +3916,8 @@ var _Loom3 = class _Loom3 {
|
|
|
3916
3916
|
__publicField(this, "bones", {});
|
|
3917
3917
|
__publicField(this, "mixWeights", {});
|
|
3918
3918
|
// Viseme state
|
|
3919
|
-
__publicField(this, "visemeValues",
|
|
3919
|
+
__publicField(this, "visemeValues", []);
|
|
3920
|
+
__publicField(this, "visemeJawScales", []);
|
|
3920
3921
|
__publicField(this, "bakedAnimations");
|
|
3921
3922
|
__publicField(this, "hairPhysics");
|
|
3922
3923
|
// Internal animation loop
|
|
@@ -3929,6 +3930,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3929
3930
|
const basePreset = config.presetType ? getPreset(config.presetType) : CC4_PRESET;
|
|
3930
3931
|
this.config = extendPresetWithProfile(basePreset, config.profile);
|
|
3931
3932
|
this.mixWeights = { ...this.config.auMixDefaults };
|
|
3933
|
+
this.syncVisemeRuntimeState();
|
|
3932
3934
|
this.animation = animation || new AnimationThree();
|
|
3933
3935
|
this.compositeRotations = this.config.compositeRotations || COMPOSITE_ROTATIONS;
|
|
3934
3936
|
this.auToCompositeMap = buildAUToCompositeMap(this.compositeRotations);
|
|
@@ -4461,6 +4463,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4461
4463
|
if (visemeIndex < 0 || visemeIndex >= this.config.visemeKeys.length) return;
|
|
4462
4464
|
const val = clamp012(value);
|
|
4463
4465
|
this.visemeValues[visemeIndex] = val;
|
|
4466
|
+
this.visemeJawScales[visemeIndex] = jawScale;
|
|
4464
4467
|
const targets = this.resolvedVisemeTargets[visemeIndex];
|
|
4465
4468
|
if (targets && targets.length > 0) {
|
|
4466
4469
|
this.applyMorphTargets(targets, val);
|
|
@@ -4473,7 +4476,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4473
4476
|
this.setMorph(morphKey, val, visemeMeshNames);
|
|
4474
4477
|
}
|
|
4475
4478
|
}
|
|
4476
|
-
const jawAmount =
|
|
4479
|
+
const jawAmount = this.getVisemeJawAmount(visemeIndex) * val * jawScale;
|
|
4477
4480
|
if (Math.abs(jawScale) > 1e-6 && Math.abs(jawAmount) > 1e-6) {
|
|
4478
4481
|
this.updateBoneRotation("JAW", "pitch", jawAmount);
|
|
4479
4482
|
}
|
|
@@ -4488,9 +4491,10 @@ var _Loom3 = class _Loom3 {
|
|
|
4488
4491
|
const morphKey = this.config.visemeKeys[visemeIndex];
|
|
4489
4492
|
const target = clamp012(to);
|
|
4490
4493
|
this.visemeValues[visemeIndex] = target;
|
|
4494
|
+
this.visemeJawScales[visemeIndex] = jawScale;
|
|
4491
4495
|
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
4492
4496
|
const morphHandle = typeof morphKey === "number" ? this.transitionMorphInfluence(morphKey, target, durationMs, visemeMeshNames) : this.transitionMorph(morphKey, target, durationMs, visemeMeshNames);
|
|
4493
|
-
const jawAmount =
|
|
4497
|
+
const jawAmount = this.getVisemeJawAmount(visemeIndex) * target * jawScale;
|
|
4494
4498
|
if (Math.abs(jawScale) <= 1e-6 || Math.abs(jawAmount) <= 1e-6) {
|
|
4495
4499
|
return morphHandle;
|
|
4496
4500
|
}
|
|
@@ -4544,6 +4548,9 @@ var _Loom3 = class _Loom3 {
|
|
|
4544
4548
|
}
|
|
4545
4549
|
resetToNeutral() {
|
|
4546
4550
|
this.auValues = {};
|
|
4551
|
+
this.visemeValues = new Array(this.config.visemeKeys.length).fill(0);
|
|
4552
|
+
this.visemeJawScales = new Array(this.config.visemeKeys.length).fill(1);
|
|
4553
|
+
this.translations = {};
|
|
4547
4554
|
this.initBoneRotations();
|
|
4548
4555
|
this.clearTransitions();
|
|
4549
4556
|
for (const m of this.meshes) {
|
|
@@ -4559,6 +4566,33 @@ var _Loom3 = class _Loom3 {
|
|
|
4559
4566
|
entry.obj.quaternion.copy(entry.baseQuat);
|
|
4560
4567
|
});
|
|
4561
4568
|
}
|
|
4569
|
+
reinitializeRuntimeStateFromCurrentControls(staleMorphTargets = []) {
|
|
4570
|
+
this.clearTransitions();
|
|
4571
|
+
this.resetMorphTargetHandles(staleMorphTargets);
|
|
4572
|
+
this.translations = {};
|
|
4573
|
+
this.initBoneRotations();
|
|
4574
|
+
Object.values(this.bones).forEach((entry) => {
|
|
4575
|
+
if (!entry) return;
|
|
4576
|
+
entry.obj.position.copy(entry.basePos);
|
|
4577
|
+
entry.obj.quaternion.copy(entry.baseQuat);
|
|
4578
|
+
entry.obj.updateMatrixWorld(false);
|
|
4579
|
+
});
|
|
4580
|
+
for (const [auIdStr, value] of Object.entries(this.auValues)) {
|
|
4581
|
+
if (value <= 0) continue;
|
|
4582
|
+
const auId = Number(auIdStr);
|
|
4583
|
+
if (Number.isNaN(auId)) continue;
|
|
4584
|
+
this.setAU(auId, value, this.auBalances[auId]);
|
|
4585
|
+
}
|
|
4586
|
+
for (let visemeIndex = 0; visemeIndex < this.visemeValues.length; visemeIndex += 1) {
|
|
4587
|
+
const value = this.visemeValues[visemeIndex] ?? 0;
|
|
4588
|
+
if (value <= 0) continue;
|
|
4589
|
+
this.setViseme(visemeIndex, value, this.visemeJawScales[visemeIndex] ?? 1);
|
|
4590
|
+
}
|
|
4591
|
+
if (this.model) {
|
|
4592
|
+
this.flushPendingComposites();
|
|
4593
|
+
this.model.updateMatrixWorld(true);
|
|
4594
|
+
}
|
|
4595
|
+
}
|
|
4562
4596
|
// ============================================================================
|
|
4563
4597
|
// MESH CONTROL
|
|
4564
4598
|
// ============================================================================
|
|
@@ -4789,12 +4823,20 @@ var _Loom3 = class _Loom3 {
|
|
|
4789
4823
|
}
|
|
4790
4824
|
setProfile(profile) {
|
|
4791
4825
|
this.config = profile;
|
|
4826
|
+
this.compositeRotations = this.config.compositeRotations || COMPOSITE_ROTATIONS;
|
|
4827
|
+
this.auToCompositeMap = buildAUToCompositeMap(this.compositeRotations);
|
|
4792
4828
|
this.mixWeights = { ...profile.auMixDefaults };
|
|
4829
|
+
this.syncVisemeRuntimeState();
|
|
4830
|
+
let staleMorphTargets = [];
|
|
4793
4831
|
if (this.model) {
|
|
4832
|
+
staleMorphTargets = this.collectResolvedExpressionMorphTargets();
|
|
4833
|
+
this.bones = this.resolveBones(this.model);
|
|
4834
|
+
this.missingBoneWarnings.clear();
|
|
4794
4835
|
this.rebuildMorphTargetsCache();
|
|
4795
4836
|
}
|
|
4796
4837
|
this.hairPhysics.refreshMeshSelection();
|
|
4797
4838
|
this.applyHairPhysicsProfileConfig();
|
|
4839
|
+
this.reinitializeRuntimeStateFromCurrentControls(staleMorphTargets);
|
|
4798
4840
|
}
|
|
4799
4841
|
getProfile() {
|
|
4800
4842
|
return this.config;
|
|
@@ -4932,6 +4974,39 @@ var _Loom3 = class _Loom3 {
|
|
|
4932
4974
|
getMorphIndexCacheKey(index, meshNames) {
|
|
4933
4975
|
return meshNames?.length ? `idx:${index}@${meshNames.join(",")}` : `idx:${index}`;
|
|
4934
4976
|
}
|
|
4977
|
+
syncVisemeRuntimeState() {
|
|
4978
|
+
const visemeCount = this.config.visemeKeys.length;
|
|
4979
|
+
this.visemeValues = Array.from(
|
|
4980
|
+
{ length: visemeCount },
|
|
4981
|
+
(_, index) => this.visemeValues[index] ?? 0
|
|
4982
|
+
);
|
|
4983
|
+
this.visemeJawScales = Array.from(
|
|
4984
|
+
{ length: visemeCount },
|
|
4985
|
+
(_, index) => this.visemeJawScales[index] ?? 1
|
|
4986
|
+
);
|
|
4987
|
+
}
|
|
4988
|
+
getVisemeJawAmount(visemeIndex) {
|
|
4989
|
+
return this.config.visemeJawAmounts?.[visemeIndex] ?? _Loom3.VISEME_JAW_AMOUNTS[visemeIndex] ?? 0;
|
|
4990
|
+
}
|
|
4991
|
+
collectResolvedExpressionMorphTargets() {
|
|
4992
|
+
const targets = [];
|
|
4993
|
+
for (const resolved of this.resolvedAUMorphTargets.values()) {
|
|
4994
|
+
targets.push(...resolved.left, ...resolved.right, ...resolved.center);
|
|
4995
|
+
}
|
|
4996
|
+
for (const resolved of this.resolvedVisemeTargets) {
|
|
4997
|
+
if (resolved?.length) {
|
|
4998
|
+
targets.push(...resolved);
|
|
4999
|
+
}
|
|
5000
|
+
}
|
|
5001
|
+
return targets;
|
|
5002
|
+
}
|
|
5003
|
+
resetMorphTargetHandles(targets) {
|
|
5004
|
+
for (const { infl, idx } of targets) {
|
|
5005
|
+
if (idx < infl.length) {
|
|
5006
|
+
infl[idx] = 0;
|
|
5007
|
+
}
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
4935
5010
|
isMixedAU(id) {
|
|
4936
5011
|
const morphs = this.config.auToMorphs[id];
|
|
4937
5012
|
const hasMorphs = !!(morphs?.left?.length || morphs?.right?.length || morphs?.center?.length);
|
|
@@ -5073,12 +5148,25 @@ var _Loom3 = class _Loom3 {
|
|
|
5073
5148
|
}
|
|
5074
5149
|
resolveBones(root) {
|
|
5075
5150
|
const resolved = {};
|
|
5151
|
+
const previousBones = this.bones;
|
|
5076
5152
|
const snapshot = (obj) => ({
|
|
5077
5153
|
obj,
|
|
5078
5154
|
basePos: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
|
|
5079
5155
|
baseQuat: obj.quaternion.clone(),
|
|
5080
5156
|
baseEuler: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, order: obj.rotation.order }
|
|
5081
5157
|
});
|
|
5158
|
+
const snapshotPreservingBasePose = (obj) => {
|
|
5159
|
+
const existing = Object.values(previousBones).find((entry) => entry?.obj === obj);
|
|
5160
|
+
if (!existing) {
|
|
5161
|
+
return snapshot(obj);
|
|
5162
|
+
}
|
|
5163
|
+
return {
|
|
5164
|
+
obj,
|
|
5165
|
+
basePos: { ...existing.basePos },
|
|
5166
|
+
baseQuat: existing.baseQuat.clone(),
|
|
5167
|
+
baseEuler: { ...existing.baseEuler }
|
|
5168
|
+
};
|
|
5169
|
+
};
|
|
5082
5170
|
const prefix = this.config.bonePrefix || "";
|
|
5083
5171
|
const suffix = this.config.boneSuffix || "";
|
|
5084
5172
|
const suffixRegex = this.config.suffixPattern ? new RegExp(this.config.suffixPattern) : null;
|
|
@@ -5109,19 +5197,19 @@ var _Loom3 = class _Loom3 {
|
|
|
5109
5197
|
for (const [key, nodeName] of Object.entries(this.config.boneNodes)) {
|
|
5110
5198
|
const node = findNode(nodeName);
|
|
5111
5199
|
if (node) {
|
|
5112
|
-
resolved[key] =
|
|
5200
|
+
resolved[key] = snapshotPreservingBasePose(node);
|
|
5113
5201
|
}
|
|
5114
5202
|
}
|
|
5115
5203
|
if (!resolved.EYE_L && this.config.eyeMeshNodes) {
|
|
5116
5204
|
const node = findNode(this.config.eyeMeshNodes.LEFT);
|
|
5117
5205
|
if (node) {
|
|
5118
|
-
resolved.EYE_L =
|
|
5206
|
+
resolved.EYE_L = snapshotPreservingBasePose(node);
|
|
5119
5207
|
}
|
|
5120
5208
|
}
|
|
5121
5209
|
if (!resolved.EYE_R && this.config.eyeMeshNodes) {
|
|
5122
5210
|
const node = findNode(this.config.eyeMeshNodes.RIGHT);
|
|
5123
5211
|
if (node) {
|
|
5124
|
-
resolved.EYE_R =
|
|
5212
|
+
resolved.EYE_R = snapshotPreservingBasePose(node);
|
|
5125
5213
|
}
|
|
5126
5214
|
}
|
|
5127
5215
|
return resolved;
|