@lovelace_lol/loom3 1.0.3 → 1.0.4
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 +92 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +92 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -158,8 +158,22 @@ var BakedAnimationController = class {
|
|
|
158
158
|
return this.host.getMeshNamesForAU(auId) || [];
|
|
159
159
|
}
|
|
160
160
|
const facePart = config.auInfo?.[String(auId)]?.facePart;
|
|
161
|
-
if (facePart
|
|
162
|
-
|
|
161
|
+
if (facePart) {
|
|
162
|
+
const category = config.auFacePartToMeshCategory?.[facePart];
|
|
163
|
+
if (category) return config.morphToMesh?.[category] || [];
|
|
164
|
+
}
|
|
165
|
+
return config.morphToMesh?.face || [];
|
|
166
|
+
}
|
|
167
|
+
getMeshNamesForViseme(config, explicitMeshNames) {
|
|
168
|
+
if (explicitMeshNames && explicitMeshNames.length > 0) {
|
|
169
|
+
return explicitMeshNames;
|
|
170
|
+
}
|
|
171
|
+
if (typeof this.host.getMeshNamesForViseme === "function") {
|
|
172
|
+
return this.host.getMeshNamesForViseme() || [];
|
|
173
|
+
}
|
|
174
|
+
const category = config.visemeMeshCategory || (config.morphToMesh?.viseme ? "viseme" : "face");
|
|
175
|
+
const visemeMeshes = config.morphToMesh?.[category];
|
|
176
|
+
if (visemeMeshes && visemeMeshes.length > 0) return visemeMeshes;
|
|
163
177
|
return config.morphToMesh?.face || [];
|
|
164
178
|
}
|
|
165
179
|
update(dtSeconds) {
|
|
@@ -455,11 +469,12 @@ var BakedAnimationController = class {
|
|
|
455
469
|
if (isNumericAU(curveId)) {
|
|
456
470
|
const auId = Number(curveId);
|
|
457
471
|
if (isVisemeIndex(curveId)) {
|
|
472
|
+
const visemeMeshNames = this.getMeshNamesForViseme(config, meshNames);
|
|
458
473
|
const visemeKey = config.visemeKeys[auId];
|
|
459
474
|
if (typeof visemeKey === "number") {
|
|
460
|
-
this.addMorphIndexTracks(tracks, visemeKey, keyframes, intensityScale,
|
|
475
|
+
this.addMorphIndexTracks(tracks, visemeKey, keyframes, intensityScale, visemeMeshNames);
|
|
461
476
|
} else if (visemeKey) {
|
|
462
|
-
this.addMorphTracks(tracks, visemeKey, keyframes, intensityScale,
|
|
477
|
+
this.addMorphTracks(tracks, visemeKey, keyframes, intensityScale, visemeMeshNames);
|
|
463
478
|
}
|
|
464
479
|
} else {
|
|
465
480
|
const auMeshNames = this.getMeshNamesForAU(auId, config, meshNames);
|
|
@@ -1784,6 +1799,12 @@ var MORPH_TO_MESH = {
|
|
|
1784
1799
|
tongue: ["CC_Base_Tongue", "CC_Base_Tongue_1"],
|
|
1785
1800
|
hair: ["Side_part_wavy", "Side_part_wavy_1", "Side_part_wavy_2"]
|
|
1786
1801
|
};
|
|
1802
|
+
var AU_FACEPART_TO_MESH_CATEGORY = {
|
|
1803
|
+
Eye: "eye",
|
|
1804
|
+
Eyes: "eye",
|
|
1805
|
+
Eyelids: "eye",
|
|
1806
|
+
Tongue: "tongue"
|
|
1807
|
+
};
|
|
1787
1808
|
var CC4_HAIR_PHYSICS = {
|
|
1788
1809
|
stiffness: 7.5,
|
|
1789
1810
|
damping: 0.18,
|
|
@@ -1829,7 +1850,9 @@ var CC4_PRESET = {
|
|
|
1829
1850
|
bonePrefix: CC4_BONE_PREFIX,
|
|
1830
1851
|
suffixPattern: CC4_SUFFIX_PATTERN,
|
|
1831
1852
|
morphToMesh: MORPH_TO_MESH,
|
|
1853
|
+
auFacePartToMeshCategory: AU_FACEPART_TO_MESH_CATEGORY,
|
|
1832
1854
|
visemeKeys: VISEME_KEYS,
|
|
1855
|
+
visemeMeshCategory: "viseme",
|
|
1833
1856
|
visemeJawAmounts: VISEME_JAW_AMOUNTS,
|
|
1834
1857
|
auMixDefaults: AU_MIX_DEFAULTS,
|
|
1835
1858
|
auInfo: AU_INFO,
|
|
@@ -2149,9 +2172,10 @@ var HairPhysicsController = class {
|
|
|
2149
2172
|
if (meshName) {
|
|
2150
2173
|
targetMesh = this.registeredHairObjects.get(meshName);
|
|
2151
2174
|
} else {
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2175
|
+
const hairMeshNames = this.getHairMeshNames();
|
|
2176
|
+
for (const name of hairMeshNames) {
|
|
2177
|
+
const mesh = this.registeredHairObjects.get(name) || this.host.getMeshByName(name);
|
|
2178
|
+
if (mesh) {
|
|
2155
2179
|
targetMesh = mesh;
|
|
2156
2180
|
break;
|
|
2157
2181
|
}
|
|
@@ -2245,6 +2269,15 @@ var HairPhysicsController = class {
|
|
|
2245
2269
|
}
|
|
2246
2270
|
getHairMeshNames() {
|
|
2247
2271
|
if (this.cachedHairMeshNames) return this.cachedHairMeshNames;
|
|
2272
|
+
if (typeof this.host.getSelectedHairMeshNames === "function") {
|
|
2273
|
+
const selectedHairMeshNames = this.host.getSelectedHairMeshNames() || [];
|
|
2274
|
+
const resolved = selectedHairMeshNames.filter((name) => {
|
|
2275
|
+
const mesh = this.registeredHairObjects.get(name) || this.host.getMeshByName(name);
|
|
2276
|
+
return !!mesh;
|
|
2277
|
+
});
|
|
2278
|
+
this.cachedHairMeshNames = Array.from(new Set(resolved));
|
|
2279
|
+
return this.cachedHairMeshNames;
|
|
2280
|
+
}
|
|
2248
2281
|
const names = [];
|
|
2249
2282
|
this.registeredHairObjects.forEach((mesh, name) => {
|
|
2250
2283
|
const info = CC4_MESHES[name];
|
|
@@ -2260,6 +2293,18 @@ var HairPhysicsController = class {
|
|
|
2260
2293
|
this.cachedHairMeshNames = names;
|
|
2261
2294
|
return names;
|
|
2262
2295
|
}
|
|
2296
|
+
refreshMeshSelection() {
|
|
2297
|
+
this.cachedHairMeshNames = null;
|
|
2298
|
+
this.idleClipDirty = true;
|
|
2299
|
+
this.gravityClipDirty = true;
|
|
2300
|
+
this.impulseClipDirty = true;
|
|
2301
|
+
this.warnMissingHairMorphTargets();
|
|
2302
|
+
if (this.hairPhysicsEnabled) {
|
|
2303
|
+
this.startIdleClip();
|
|
2304
|
+
this.startGravityClip();
|
|
2305
|
+
this.buildImpulseClips();
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2263
2308
|
supportsMixerClips() {
|
|
2264
2309
|
return typeof this.host.buildClip === "function";
|
|
2265
2310
|
}
|
|
@@ -2274,7 +2319,11 @@ var HairPhysicsController = class {
|
|
|
2274
2319
|
return;
|
|
2275
2320
|
}
|
|
2276
2321
|
const hairMeshNames = this.getHairMeshNames();
|
|
2277
|
-
if (hairMeshNames.length === 0)
|
|
2322
|
+
if (hairMeshNames.length === 0) {
|
|
2323
|
+
this.stopIdleClip();
|
|
2324
|
+
this.idleClipDirty = false;
|
|
2325
|
+
return;
|
|
2326
|
+
}
|
|
2278
2327
|
if (!this.idleClipDirty && this.idleClipHandle) return;
|
|
2279
2328
|
this.stopIdleClip();
|
|
2280
2329
|
const duration = Math.max(0.5, cfg.idleClipDuration);
|
|
@@ -2292,7 +2341,11 @@ var HairPhysicsController = class {
|
|
|
2292
2341
|
startGravityClip() {
|
|
2293
2342
|
if (!this.hairPhysicsEnabled || !this.supportsMixerClips()) return;
|
|
2294
2343
|
const hairMeshNames = this.getHairMeshNames();
|
|
2295
|
-
if (hairMeshNames.length === 0)
|
|
2344
|
+
if (hairMeshNames.length === 0) {
|
|
2345
|
+
this.stopGravityClip();
|
|
2346
|
+
this.gravityClipDirty = false;
|
|
2347
|
+
return;
|
|
2348
|
+
}
|
|
2296
2349
|
if (!this.gravityClipDirty && this.gravityClipHandle) return;
|
|
2297
2350
|
this.stopGravityClip();
|
|
2298
2351
|
const morphTargets = this.hairPhysicsConfig.morphTargets;
|
|
@@ -2345,7 +2398,11 @@ var HairPhysicsController = class {
|
|
|
2345
2398
|
buildImpulseClips() {
|
|
2346
2399
|
if (!this.hairPhysicsEnabled || !this.supportsMixerClips()) return;
|
|
2347
2400
|
const hairMeshNames = this.getHairMeshNames();
|
|
2348
|
-
if (hairMeshNames.length === 0)
|
|
2401
|
+
if (hairMeshNames.length === 0) {
|
|
2402
|
+
this.stopImpulseClips();
|
|
2403
|
+
this.impulseClipDirty = false;
|
|
2404
|
+
return;
|
|
2405
|
+
}
|
|
2349
2406
|
if (!this.impulseClipDirty && this.impulseClips.left && this.impulseClips.right && this.impulseClips.front) {
|
|
2350
2407
|
return;
|
|
2351
2408
|
}
|
|
@@ -2681,7 +2738,9 @@ function resolveProfile(base, override) {
|
|
|
2681
2738
|
auToBones: mergeRecord(base.auToBones, override.auToBones),
|
|
2682
2739
|
boneNodes: mergeRecord(base.boneNodes, override.boneNodes),
|
|
2683
2740
|
morphToMesh: mergeRecord(base.morphToMesh, override.morphToMesh),
|
|
2741
|
+
auFacePartToMeshCategory: base.auFacePartToMeshCategory || override.auFacePartToMeshCategory ? mergeRecord(base.auFacePartToMeshCategory || {}, override.auFacePartToMeshCategory || {}) : void 0,
|
|
2684
2742
|
visemeKeys: override.visemeKeys ? [...override.visemeKeys] : [...base.visemeKeys],
|
|
2743
|
+
visemeMeshCategory: override.visemeMeshCategory ?? base.visemeMeshCategory,
|
|
2685
2744
|
auMixDefaults: base.auMixDefaults || override.auMixDefaults ? mergeRecord(base.auMixDefaults || {}, override.auMixDefaults || {}) : void 0,
|
|
2686
2745
|
auInfo: base.auInfo || override.auInfo ? mergeRecord(base.auInfo || {}, override.auInfo || {}) : void 0,
|
|
2687
2746
|
eyeMeshNodes: override.eyeMeshNodes ?? base.eyeMeshNodes,
|
|
@@ -3519,6 +3578,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3519
3578
|
this.auToCompositeMap = buildAUToCompositeMap(this.compositeRotations);
|
|
3520
3579
|
this.hairPhysics = new HairPhysicsController({
|
|
3521
3580
|
getMeshByName: (name) => this.meshByName.get(name),
|
|
3581
|
+
getSelectedHairMeshNames: () => this.config.morphToMesh?.hair || [],
|
|
3522
3582
|
buildClip: (clipName, curves, options) => this.buildClip(clipName, curves, options),
|
|
3523
3583
|
cleanupSnippet: (name) => this.cleanupSnippet(name)
|
|
3524
3584
|
});
|
|
@@ -3527,6 +3587,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3527
3587
|
getMeshes: () => this.meshes,
|
|
3528
3588
|
getMeshByName: (name) => this.meshByName.get(name),
|
|
3529
3589
|
getMeshNamesForAU: (auId) => this.getMeshNamesForAU(auId),
|
|
3590
|
+
getMeshNamesForViseme: () => this.getMeshNamesForViseme(),
|
|
3530
3591
|
getBones: () => this.bones,
|
|
3531
3592
|
getConfig: () => this.config,
|
|
3532
3593
|
getCompositeRotations: () => this.compositeRotations,
|
|
@@ -3623,7 +3684,8 @@ var _Loom3 = class _Loom3 {
|
|
|
3623
3684
|
}
|
|
3624
3685
|
for (let i = 0; i < (this.config.visemeKeys || []).length; i += 1) {
|
|
3625
3686
|
const key = this.config.visemeKeys[i];
|
|
3626
|
-
const
|
|
3687
|
+
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
3688
|
+
const targets = typeof key === "number" ? this.resolveMorphTargetsByIndex(key, visemeMeshNames) : this.resolveMorphTargets(key, visemeMeshNames);
|
|
3627
3689
|
this.resolvedVisemeTargets[i] = targets;
|
|
3628
3690
|
}
|
|
3629
3691
|
}
|
|
@@ -4055,10 +4117,11 @@ var _Loom3 = class _Loom3 {
|
|
|
4055
4117
|
this.applyMorphTargets(targets, val);
|
|
4056
4118
|
} else {
|
|
4057
4119
|
const morphKey = this.config.visemeKeys[visemeIndex];
|
|
4120
|
+
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
4058
4121
|
if (typeof morphKey === "number") {
|
|
4059
|
-
this.setMorphInfluence(morphKey, val);
|
|
4122
|
+
this.setMorphInfluence(morphKey, val, visemeMeshNames);
|
|
4060
4123
|
} else if (typeof morphKey === "string") {
|
|
4061
|
-
this.setMorph(morphKey, val);
|
|
4124
|
+
this.setMorph(morphKey, val, visemeMeshNames);
|
|
4062
4125
|
}
|
|
4063
4126
|
}
|
|
4064
4127
|
const jawAmount = _Loom3.VISEME_JAW_AMOUNTS[visemeIndex] * val * jawScale;
|
|
@@ -4076,7 +4139,8 @@ var _Loom3 = class _Loom3 {
|
|
|
4076
4139
|
const morphKey = this.config.visemeKeys[visemeIndex];
|
|
4077
4140
|
const target = clamp012(to);
|
|
4078
4141
|
this.visemeValues[visemeIndex] = target;
|
|
4079
|
-
const
|
|
4142
|
+
const visemeMeshNames = this.getMeshNamesForViseme();
|
|
4143
|
+
const morphHandle = typeof morphKey === "number" ? this.transitionMorphInfluence(morphKey, target, durationMs, visemeMeshNames) : this.transitionMorph(morphKey, target, durationMs, visemeMeshNames);
|
|
4080
4144
|
const jawAmount = _Loom3.VISEME_JAW_AMOUNTS[visemeIndex] * target * jawScale;
|
|
4081
4145
|
if (Math.abs(jawScale) <= 1e-6 || Math.abs(jawAmount) <= 1e-6) {
|
|
4082
4146
|
return morphHandle;
|
|
@@ -4380,6 +4444,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4380
4444
|
if (this.model) {
|
|
4381
4445
|
this.rebuildMorphTargetsCache();
|
|
4382
4446
|
}
|
|
4447
|
+
this.hairPhysics.refreshMeshSelection();
|
|
4383
4448
|
this.applyHairPhysicsProfileConfig();
|
|
4384
4449
|
}
|
|
4385
4450
|
getProfile() {
|
|
@@ -4387,20 +4452,24 @@ var _Loom3 = class _Loom3 {
|
|
|
4387
4452
|
}
|
|
4388
4453
|
/**
|
|
4389
4454
|
* Get the mesh names that should receive morph influences for a given AU.
|
|
4390
|
-
*
|
|
4455
|
+
* Routing is driven by `auFacePartToMeshCategory` in profile config.
|
|
4391
4456
|
*/
|
|
4392
4457
|
getMeshNamesForAU(auId) {
|
|
4393
4458
|
const m = this.config.morphToMesh;
|
|
4394
4459
|
const info = this.config.auInfo?.[String(auId)];
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
default:
|
|
4402
|
-
return m?.face || [];
|
|
4460
|
+
const facePart = info?.facePart;
|
|
4461
|
+
if (facePart) {
|
|
4462
|
+
const category = this.config.auFacePartToMeshCategory?.[facePart];
|
|
4463
|
+
if (category) {
|
|
4464
|
+
return m?.[category] || [];
|
|
4465
|
+
}
|
|
4403
4466
|
}
|
|
4467
|
+
return m?.face || [];
|
|
4468
|
+
}
|
|
4469
|
+
getMeshNamesForViseme() {
|
|
4470
|
+
const m = this.config.morphToMesh;
|
|
4471
|
+
const category = this.config.visemeMeshCategory || (m?.viseme ? "viseme" : "face");
|
|
4472
|
+
return m?.[category] || m?.face || [];
|
|
4404
4473
|
}
|
|
4405
4474
|
// ============================================================================
|
|
4406
4475
|
// HAIR PHYSICS
|