@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.cjs
CHANGED
|
@@ -3937,7 +3937,8 @@ var _Loom3 = class _Loom3 {
|
|
|
3937
3937
|
__publicField(this, "bones", {});
|
|
3938
3938
|
__publicField(this, "mixWeights", {});
|
|
3939
3939
|
// Viseme state
|
|
3940
|
-
__publicField(this, "visemeValues",
|
|
3940
|
+
__publicField(this, "visemeValues", []);
|
|
3941
|
+
__publicField(this, "visemeJawScales", []);
|
|
3941
3942
|
__publicField(this, "bakedAnimations");
|
|
3942
3943
|
__publicField(this, "hairPhysics");
|
|
3943
3944
|
// Internal animation loop
|
|
@@ -3950,6 +3951,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3950
3951
|
const basePreset = config.presetType ? getPreset(config.presetType) : CC4_PRESET;
|
|
3951
3952
|
this.config = extendPresetWithProfile(basePreset, config.profile);
|
|
3952
3953
|
this.mixWeights = { ...this.config.auMixDefaults };
|
|
3954
|
+
this.syncVisemeRuntimeState();
|
|
3953
3955
|
this.animation = animation || new AnimationThree();
|
|
3954
3956
|
this.compositeRotations = this.config.compositeRotations || COMPOSITE_ROTATIONS;
|
|
3955
3957
|
this.auToCompositeMap = buildAUToCompositeMap(this.compositeRotations);
|
|
@@ -4482,6 +4484,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4482
4484
|
if (visemeIndex < 0 || visemeIndex >= this.config.visemeKeys.length) return;
|
|
4483
4485
|
const val = clamp012(value);
|
|
4484
4486
|
this.visemeValues[visemeIndex] = val;
|
|
4487
|
+
this.visemeJawScales[visemeIndex] = jawScale;
|
|
4485
4488
|
const targets = this.resolvedVisemeTargets[visemeIndex];
|
|
4486
4489
|
if (targets && targets.length > 0) {
|
|
4487
4490
|
this.applyMorphTargets(targets, val);
|
|
@@ -4494,7 +4497,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4494
4497
|
this.setMorph(morphKey, val, visemeMeshNames);
|
|
4495
4498
|
}
|
|
4496
4499
|
}
|
|
4497
|
-
const jawAmount =
|
|
4500
|
+
const jawAmount = this.getVisemeJawAmount(visemeIndex) * val * jawScale;
|
|
4498
4501
|
if (Math.abs(jawScale) > 1e-6 && Math.abs(jawAmount) > 1e-6) {
|
|
4499
4502
|
this.updateBoneRotation("JAW", "pitch", jawAmount);
|
|
4500
4503
|
}
|
|
@@ -4509,9 +4512,10 @@ var _Loom3 = class _Loom3 {
|
|
|
4509
4512
|
const morphKey = this.config.visemeKeys[visemeIndex];
|
|
4510
4513
|
const target = clamp012(to);
|
|
4511
4514
|
this.visemeValues[visemeIndex] = target;
|
|
4515
|
+
this.visemeJawScales[visemeIndex] = jawScale;
|
|
4512
4516
|
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
4513
4517
|
const morphHandle = typeof morphKey === "number" ? this.transitionMorphInfluence(morphKey, target, durationMs, visemeMeshNames) : this.transitionMorph(morphKey, target, durationMs, visemeMeshNames);
|
|
4514
|
-
const jawAmount =
|
|
4518
|
+
const jawAmount = this.getVisemeJawAmount(visemeIndex) * target * jawScale;
|
|
4515
4519
|
if (Math.abs(jawScale) <= 1e-6 || Math.abs(jawAmount) <= 1e-6) {
|
|
4516
4520
|
return morphHandle;
|
|
4517
4521
|
}
|
|
@@ -4565,6 +4569,9 @@ var _Loom3 = class _Loom3 {
|
|
|
4565
4569
|
}
|
|
4566
4570
|
resetToNeutral() {
|
|
4567
4571
|
this.auValues = {};
|
|
4572
|
+
this.visemeValues = new Array(this.config.visemeKeys.length).fill(0);
|
|
4573
|
+
this.visemeJawScales = new Array(this.config.visemeKeys.length).fill(1);
|
|
4574
|
+
this.translations = {};
|
|
4568
4575
|
this.initBoneRotations();
|
|
4569
4576
|
this.clearTransitions();
|
|
4570
4577
|
for (const m of this.meshes) {
|
|
@@ -4580,6 +4587,33 @@ var _Loom3 = class _Loom3 {
|
|
|
4580
4587
|
entry.obj.quaternion.copy(entry.baseQuat);
|
|
4581
4588
|
});
|
|
4582
4589
|
}
|
|
4590
|
+
reinitializeRuntimeStateFromCurrentControls(staleMorphTargets = []) {
|
|
4591
|
+
this.clearTransitions();
|
|
4592
|
+
this.resetMorphTargetHandles(staleMorphTargets);
|
|
4593
|
+
this.translations = {};
|
|
4594
|
+
this.initBoneRotations();
|
|
4595
|
+
Object.values(this.bones).forEach((entry) => {
|
|
4596
|
+
if (!entry) return;
|
|
4597
|
+
entry.obj.position.copy(entry.basePos);
|
|
4598
|
+
entry.obj.quaternion.copy(entry.baseQuat);
|
|
4599
|
+
entry.obj.updateMatrixWorld(false);
|
|
4600
|
+
});
|
|
4601
|
+
for (const [auIdStr, value] of Object.entries(this.auValues)) {
|
|
4602
|
+
if (value <= 0) continue;
|
|
4603
|
+
const auId = Number(auIdStr);
|
|
4604
|
+
if (Number.isNaN(auId)) continue;
|
|
4605
|
+
this.setAU(auId, value, this.auBalances[auId]);
|
|
4606
|
+
}
|
|
4607
|
+
for (let visemeIndex = 0; visemeIndex < this.visemeValues.length; visemeIndex += 1) {
|
|
4608
|
+
const value = this.visemeValues[visemeIndex] ?? 0;
|
|
4609
|
+
if (value <= 0) continue;
|
|
4610
|
+
this.setViseme(visemeIndex, value, this.visemeJawScales[visemeIndex] ?? 1);
|
|
4611
|
+
}
|
|
4612
|
+
if (this.model) {
|
|
4613
|
+
this.flushPendingComposites();
|
|
4614
|
+
this.model.updateMatrixWorld(true);
|
|
4615
|
+
}
|
|
4616
|
+
}
|
|
4583
4617
|
// ============================================================================
|
|
4584
4618
|
// MESH CONTROL
|
|
4585
4619
|
// ============================================================================
|
|
@@ -4810,12 +4844,20 @@ var _Loom3 = class _Loom3 {
|
|
|
4810
4844
|
}
|
|
4811
4845
|
setProfile(profile) {
|
|
4812
4846
|
this.config = profile;
|
|
4847
|
+
this.compositeRotations = this.config.compositeRotations || COMPOSITE_ROTATIONS;
|
|
4848
|
+
this.auToCompositeMap = buildAUToCompositeMap(this.compositeRotations);
|
|
4813
4849
|
this.mixWeights = { ...profile.auMixDefaults };
|
|
4850
|
+
this.syncVisemeRuntimeState();
|
|
4851
|
+
let staleMorphTargets = [];
|
|
4814
4852
|
if (this.model) {
|
|
4853
|
+
staleMorphTargets = this.collectResolvedExpressionMorphTargets();
|
|
4854
|
+
this.bones = this.resolveBones(this.model);
|
|
4855
|
+
this.missingBoneWarnings.clear();
|
|
4815
4856
|
this.rebuildMorphTargetsCache();
|
|
4816
4857
|
}
|
|
4817
4858
|
this.hairPhysics.refreshMeshSelection();
|
|
4818
4859
|
this.applyHairPhysicsProfileConfig();
|
|
4860
|
+
this.reinitializeRuntimeStateFromCurrentControls(staleMorphTargets);
|
|
4819
4861
|
}
|
|
4820
4862
|
getProfile() {
|
|
4821
4863
|
return this.config;
|
|
@@ -4953,6 +4995,39 @@ var _Loom3 = class _Loom3 {
|
|
|
4953
4995
|
getMorphIndexCacheKey(index, meshNames) {
|
|
4954
4996
|
return meshNames?.length ? `idx:${index}@${meshNames.join(",")}` : `idx:${index}`;
|
|
4955
4997
|
}
|
|
4998
|
+
syncVisemeRuntimeState() {
|
|
4999
|
+
const visemeCount = this.config.visemeKeys.length;
|
|
5000
|
+
this.visemeValues = Array.from(
|
|
5001
|
+
{ length: visemeCount },
|
|
5002
|
+
(_, index) => this.visemeValues[index] ?? 0
|
|
5003
|
+
);
|
|
5004
|
+
this.visemeJawScales = Array.from(
|
|
5005
|
+
{ length: visemeCount },
|
|
5006
|
+
(_, index) => this.visemeJawScales[index] ?? 1
|
|
5007
|
+
);
|
|
5008
|
+
}
|
|
5009
|
+
getVisemeJawAmount(visemeIndex) {
|
|
5010
|
+
return this.config.visemeJawAmounts?.[visemeIndex] ?? _Loom3.VISEME_JAW_AMOUNTS[visemeIndex] ?? 0;
|
|
5011
|
+
}
|
|
5012
|
+
collectResolvedExpressionMorphTargets() {
|
|
5013
|
+
const targets = [];
|
|
5014
|
+
for (const resolved of this.resolvedAUMorphTargets.values()) {
|
|
5015
|
+
targets.push(...resolved.left, ...resolved.right, ...resolved.center);
|
|
5016
|
+
}
|
|
5017
|
+
for (const resolved of this.resolvedVisemeTargets) {
|
|
5018
|
+
if (resolved?.length) {
|
|
5019
|
+
targets.push(...resolved);
|
|
5020
|
+
}
|
|
5021
|
+
}
|
|
5022
|
+
return targets;
|
|
5023
|
+
}
|
|
5024
|
+
resetMorphTargetHandles(targets) {
|
|
5025
|
+
for (const { infl, idx } of targets) {
|
|
5026
|
+
if (idx < infl.length) {
|
|
5027
|
+
infl[idx] = 0;
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
4956
5031
|
isMixedAU(id) {
|
|
4957
5032
|
const morphs = this.config.auToMorphs[id];
|
|
4958
5033
|
const hasMorphs = !!(morphs?.left?.length || morphs?.right?.length || morphs?.center?.length);
|
|
@@ -5094,12 +5169,25 @@ var _Loom3 = class _Loom3 {
|
|
|
5094
5169
|
}
|
|
5095
5170
|
resolveBones(root) {
|
|
5096
5171
|
const resolved = {};
|
|
5172
|
+
const previousBones = this.bones;
|
|
5097
5173
|
const snapshot = (obj) => ({
|
|
5098
5174
|
obj,
|
|
5099
5175
|
basePos: { x: obj.position.x, y: obj.position.y, z: obj.position.z },
|
|
5100
5176
|
baseQuat: obj.quaternion.clone(),
|
|
5101
5177
|
baseEuler: { x: obj.rotation.x, y: obj.rotation.y, z: obj.rotation.z, order: obj.rotation.order }
|
|
5102
5178
|
});
|
|
5179
|
+
const snapshotPreservingBasePose = (obj) => {
|
|
5180
|
+
const existing = Object.values(previousBones).find((entry) => entry?.obj === obj);
|
|
5181
|
+
if (!existing) {
|
|
5182
|
+
return snapshot(obj);
|
|
5183
|
+
}
|
|
5184
|
+
return {
|
|
5185
|
+
obj,
|
|
5186
|
+
basePos: { ...existing.basePos },
|
|
5187
|
+
baseQuat: existing.baseQuat.clone(),
|
|
5188
|
+
baseEuler: { ...existing.baseEuler }
|
|
5189
|
+
};
|
|
5190
|
+
};
|
|
5103
5191
|
const prefix = this.config.bonePrefix || "";
|
|
5104
5192
|
const suffix = this.config.boneSuffix || "";
|
|
5105
5193
|
const suffixRegex = this.config.suffixPattern ? new RegExp(this.config.suffixPattern) : null;
|
|
@@ -5130,19 +5218,19 @@ var _Loom3 = class _Loom3 {
|
|
|
5130
5218
|
for (const [key, nodeName] of Object.entries(this.config.boneNodes)) {
|
|
5131
5219
|
const node = findNode(nodeName);
|
|
5132
5220
|
if (node) {
|
|
5133
|
-
resolved[key] =
|
|
5221
|
+
resolved[key] = snapshotPreservingBasePose(node);
|
|
5134
5222
|
}
|
|
5135
5223
|
}
|
|
5136
5224
|
if (!resolved.EYE_L && this.config.eyeMeshNodes) {
|
|
5137
5225
|
const node = findNode(this.config.eyeMeshNodes.LEFT);
|
|
5138
5226
|
if (node) {
|
|
5139
|
-
resolved.EYE_L =
|
|
5227
|
+
resolved.EYE_L = snapshotPreservingBasePose(node);
|
|
5140
5228
|
}
|
|
5141
5229
|
}
|
|
5142
5230
|
if (!resolved.EYE_R && this.config.eyeMeshNodes) {
|
|
5143
5231
|
const node = findNode(this.config.eyeMeshNodes.RIGHT);
|
|
5144
5232
|
if (node) {
|
|
5145
|
-
resolved.EYE_R =
|
|
5233
|
+
resolved.EYE_R = snapshotPreservingBasePose(node);
|
|
5146
5234
|
}
|
|
5147
5235
|
}
|
|
5148
5236
|
return resolved;
|