@lovelace_lol/loom3 1.0.32 → 1.0.34
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 +14 -14
- package/dist/index.cjs +80 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -24
- package/dist/index.d.ts +20 -24
- package/dist/index.js +78 -50
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,9 +27,9 @@ Use the README in one of these paths:
|
|
|
27
27
|
- Skeletal-only character: [Creating Skeletal Animation Presets](#6-creating-skeletal-animation-presets) -> [Baked Animations](#16-baked-animations) -> [Regions & Geometry Helpers](#17-regions--geometry-helpers)
|
|
28
28
|
- Annotation or camera tooling: [Preset Selection & Validation](#3-preset-selection--validation) -> [Getting to Know Your Character](#4-getting-to-know-your-character) -> [Regions & Geometry Helpers](#17-regions--geometry-helpers)
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## Demo Site Links
|
|
31
31
|
|
|
32
|
-
These links open the
|
|
32
|
+
These demo site links open the LoomLarge drawer on the matching tab. The demo site currently supports stable `drawer` + `tab` deep links, so the README should lean on tab-specific links instead of pretending it can deep-link to a fully reconstructed authoring state.
|
|
33
33
|
|
|
34
34
|
| Goal | Open in LoomLarge |
|
|
35
35
|
|------|-------------------|
|
|
@@ -308,7 +308,7 @@ import { Loom3, CC4_PRESET } from '@lovelace_lol/loom3';
|
|
|
308
308
|
const loom = new Loom3({ profile: CC4_PRESET });
|
|
309
309
|
```
|
|
310
310
|
|
|
311
|
-
You can also
|
|
311
|
+
You can also look up presets by name and extend them without cloning the full preset:
|
|
312
312
|
|
|
313
313
|
```typescript
|
|
314
314
|
import { Loom3 } from '@lovelace_lol/loom3';
|
|
@@ -386,13 +386,13 @@ Use preset helpers when you want a stable entry point by model class instead of
|
|
|
386
386
|
|
|
387
387
|
```typescript
|
|
388
388
|
import {
|
|
389
|
-
|
|
390
|
-
|
|
389
|
+
getPreset,
|
|
390
|
+
getPresetWithProfile,
|
|
391
391
|
} from '@lovelace_lol/loom3';
|
|
392
392
|
|
|
393
|
-
const preset =
|
|
393
|
+
const preset = getPreset('cc4');
|
|
394
394
|
|
|
395
|
-
const
|
|
395
|
+
const extended = getPresetWithProfile('cc4', {
|
|
396
396
|
morphToMesh: { face: ['Object_9'] },
|
|
397
397
|
});
|
|
398
398
|
```
|
|
@@ -566,10 +566,10 @@ import {
|
|
|
566
566
|
analyzeModel,
|
|
567
567
|
validateMappings,
|
|
568
568
|
generateMappingCorrections,
|
|
569
|
-
|
|
569
|
+
getPreset,
|
|
570
570
|
} from '@lovelace_lol/loom3';
|
|
571
571
|
|
|
572
|
-
const preset =
|
|
572
|
+
const preset = getPreset('cc4');
|
|
573
573
|
const modelData = extractModelData(model, meshes, animations);
|
|
574
574
|
const gltfData = extractFromGLTF(gltf); // Same ModelData shape, one-step GLTF wrapper
|
|
575
575
|
|
|
@@ -673,12 +673,12 @@ Open in LoomLarge: [Properties tab](https://loomlarge.web.app/?drawer=open&tab=p
|
|
|
673
673
|
|
|
674
674
|
### Extending an existing preset
|
|
675
675
|
|
|
676
|
-
Use `
|
|
676
|
+
Use `extendPresetWithProfile` to override specific mappings while keeping the rest:
|
|
677
677
|
|
|
678
678
|
```typescript
|
|
679
|
-
import { CC4_PRESET,
|
|
679
|
+
import { CC4_PRESET, extendPresetWithProfile } from '@lovelace_lol/loom3';
|
|
680
680
|
|
|
681
|
-
const MY_PRESET =
|
|
681
|
+
const MY_PRESET = extendPresetWithProfile(CC4_PRESET, {
|
|
682
682
|
|
|
683
683
|
// Override AU12 (smile) with custom morph names
|
|
684
684
|
auToMorphs: {
|
|
@@ -2117,8 +2117,8 @@ This is a compact reference for the public surface exported by `@lovelace_lol/lo
|
|
|
2117
2117
|
|
|
2118
2118
|
### Presets and profiles
|
|
2119
2119
|
|
|
2120
|
-
- Presets: `CC4_PRESET`, `BETTA_FISH_PRESET`, `
|
|
2121
|
-
- Profile composition: `
|
|
2120
|
+
- Presets: `CC4_PRESET`, `BETTA_FISH_PRESET`, `getPreset()`, `getPresetWithProfile()`.
|
|
2121
|
+
- Profile composition: `extendPresetWithProfile()`.
|
|
2122
2122
|
- CC4 exports: `VISEME_KEYS`, `VISEME_JAW_AMOUNTS`, `CONTINUUM_PAIRS_MAP`, `CONTINUUM_LABELS`, `AU_INFO`, `COMPOSITE_ROTATIONS`, `AU_MIX_DEFAULTS`.
|
|
2123
2123
|
- Compatibility helpers: `isMixedAU()`, `hasLeftRightMorphs()`.
|
|
2124
2124
|
|
package/dist/index.cjs
CHANGED
|
@@ -2900,7 +2900,7 @@ var HairPhysicsController = class {
|
|
|
2900
2900
|
}
|
|
2901
2901
|
};
|
|
2902
2902
|
|
|
2903
|
-
// src/mappings/
|
|
2903
|
+
// src/mappings/extendPresetWithProfile.ts
|
|
2904
2904
|
var isPlainObject = (value) => {
|
|
2905
2905
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2906
2906
|
};
|
|
@@ -2992,31 +2992,33 @@ var mergeHairPhysicsConfig = (base, override) => {
|
|
|
2992
2992
|
}
|
|
2993
2993
|
return merged;
|
|
2994
2994
|
};
|
|
2995
|
-
function
|
|
2995
|
+
function extendPresetWithProfile(base, extension) {
|
|
2996
|
+
if (!extension) {
|
|
2997
|
+
return base;
|
|
2998
|
+
}
|
|
2999
|
+
const disabledRegions = extension.disabledRegions ? [...extension.disabledRegions] : base.disabledRegions ? [...base.disabledRegions] : void 0;
|
|
2996
3000
|
return {
|
|
2997
3001
|
...base,
|
|
2998
|
-
...
|
|
2999
|
-
auToMorphs: mergeRecord(base.auToMorphs,
|
|
3000
|
-
auToBones: mergeRecord(base.auToBones,
|
|
3001
|
-
boneNodes: mergeRecord(base.boneNodes,
|
|
3002
|
-
morphToMesh: mergeRecord(base.morphToMesh,
|
|
3003
|
-
auFacePartToMeshCategory: base.auFacePartToMeshCategory ||
|
|
3004
|
-
visemeKeys:
|
|
3005
|
-
visemeMeshCategory:
|
|
3006
|
-
auMixDefaults: base.auMixDefaults ||
|
|
3007
|
-
auInfo: base.auInfo ||
|
|
3008
|
-
eyeMeshNodes:
|
|
3009
|
-
compositeRotations:
|
|
3010
|
-
meshes: base.meshes ||
|
|
3011
|
-
continuumPairs: base.continuumPairs ||
|
|
3012
|
-
continuumLabels: base.continuumLabels ||
|
|
3013
|
-
annotationRegions: mergeAnnotationRegions(base.annotationRegions,
|
|
3014
|
-
|
|
3002
|
+
...extension,
|
|
3003
|
+
auToMorphs: mergeRecord(base.auToMorphs, extension.auToMorphs),
|
|
3004
|
+
auToBones: mergeRecord(base.auToBones, extension.auToBones),
|
|
3005
|
+
boneNodes: mergeRecord(base.boneNodes, extension.boneNodes),
|
|
3006
|
+
morphToMesh: mergeRecord(base.morphToMesh, extension.morphToMesh),
|
|
3007
|
+
auFacePartToMeshCategory: base.auFacePartToMeshCategory || extension.auFacePartToMeshCategory ? mergeRecord(base.auFacePartToMeshCategory || {}, extension.auFacePartToMeshCategory || {}) : void 0,
|
|
3008
|
+
visemeKeys: extension.visemeKeys ? [...extension.visemeKeys] : [...base.visemeKeys],
|
|
3009
|
+
visemeMeshCategory: extension.visemeMeshCategory ?? base.visemeMeshCategory,
|
|
3010
|
+
auMixDefaults: base.auMixDefaults || extension.auMixDefaults ? mergeRecord(base.auMixDefaults || {}, extension.auMixDefaults || {}) : void 0,
|
|
3011
|
+
auInfo: base.auInfo || extension.auInfo ? mergeRecord(base.auInfo || {}, extension.auInfo || {}) : void 0,
|
|
3012
|
+
eyeMeshNodes: extension.eyeMeshNodes ?? base.eyeMeshNodes,
|
|
3013
|
+
compositeRotations: extension.compositeRotations ?? base.compositeRotations,
|
|
3014
|
+
meshes: base.meshes || extension.meshes ? mergeRecord(base.meshes || {}, extension.meshes || {}) : void 0,
|
|
3015
|
+
continuumPairs: base.continuumPairs || extension.continuumPairs ? mergeRecord(base.continuumPairs || {}, extension.continuumPairs || {}) : void 0,
|
|
3016
|
+
continuumLabels: base.continuumLabels || extension.continuumLabels ? mergeRecord(base.continuumLabels || {}, extension.continuumLabels || {}) : void 0,
|
|
3017
|
+
annotationRegions: mergeAnnotationRegions(base.annotationRegions, extension.annotationRegions),
|
|
3018
|
+
disabledRegions,
|
|
3019
|
+
hairPhysics: mergeHairPhysicsConfig(base.hairPhysics, extension.hairPhysics)
|
|
3015
3020
|
};
|
|
3016
3021
|
}
|
|
3017
|
-
function applyProfileToPreset(base, override) {
|
|
3018
|
-
return override ? resolveProfile(base, override) : base;
|
|
3019
|
-
}
|
|
3020
3022
|
|
|
3021
3023
|
// src/presets/bettaFish.ts
|
|
3022
3024
|
var BONES = [
|
|
@@ -3803,22 +3805,24 @@ var ANNOTATION_REGIONS = [
|
|
|
3803
3805
|
name: "gills",
|
|
3804
3806
|
bones: ["GILL_L", "GILL_R"],
|
|
3805
3807
|
paddingFactor: 1.6,
|
|
3806
|
-
children: ["
|
|
3808
|
+
children: ["gill_left", "gill_right"],
|
|
3807
3809
|
expandAnimation: "outward"
|
|
3808
3810
|
},
|
|
3809
3811
|
{
|
|
3810
|
-
name: "
|
|
3812
|
+
name: "gill_left",
|
|
3811
3813
|
bones: ["GILL_L", "GILL_L_MID", "GILL_L_TIP"],
|
|
3812
3814
|
parent: "gills",
|
|
3813
3815
|
paddingFactor: 1.4,
|
|
3814
|
-
cameraAngle: 270
|
|
3816
|
+
cameraAngle: 270,
|
|
3817
|
+
style: { lineDirection: "left" }
|
|
3815
3818
|
},
|
|
3816
3819
|
{
|
|
3817
|
-
name: "
|
|
3820
|
+
name: "gill_right",
|
|
3818
3821
|
bones: ["GILL_R", "GILL_R_MID", "GILL_R_TIP"],
|
|
3819
3822
|
parent: "gills",
|
|
3820
3823
|
paddingFactor: 1.4,
|
|
3821
|
-
cameraAngle: 90
|
|
3824
|
+
cameraAngle: 90,
|
|
3825
|
+
style: { lineDirection: "right" }
|
|
3822
3826
|
}
|
|
3823
3827
|
];
|
|
3824
3828
|
var BETTA_FISH_PROFILE = {
|
|
@@ -3855,7 +3859,7 @@ var AU_MAPPING_CONFIG = BETTA_FISH_PRESET;
|
|
|
3855
3859
|
var FISH_AU_MAPPING_CONFIG = BETTA_FISH_PRESET;
|
|
3856
3860
|
|
|
3857
3861
|
// src/presets/index.ts
|
|
3858
|
-
function
|
|
3862
|
+
function getPreset(presetType) {
|
|
3859
3863
|
switch (presetType) {
|
|
3860
3864
|
case "fish":
|
|
3861
3865
|
case "skeletal":
|
|
@@ -3866,9 +3870,8 @@ function resolvePreset(presetType) {
|
|
|
3866
3870
|
return CC4_PRESET;
|
|
3867
3871
|
}
|
|
3868
3872
|
}
|
|
3869
|
-
function
|
|
3870
|
-
|
|
3871
|
-
return applyProfileToPreset(base, overrides);
|
|
3873
|
+
function getPresetWithProfile(presetType, profile) {
|
|
3874
|
+
return extendPresetWithProfile(getPreset(presetType), profile);
|
|
3872
3875
|
}
|
|
3873
3876
|
|
|
3874
3877
|
// src/engines/three/Loom3.ts
|
|
@@ -3944,8 +3947,8 @@ var _Loom3 = class _Loom3 {
|
|
|
3944
3947
|
__publicField(this, "isRunning", false);
|
|
3945
3948
|
/** Store original emissive colors for highlight reset */
|
|
3946
3949
|
__publicField(this, "originalEmissive", /* @__PURE__ */ new Map());
|
|
3947
|
-
const basePreset = config.presetType ?
|
|
3948
|
-
this.config =
|
|
3950
|
+
const basePreset = config.presetType ? getPreset(config.presetType) : CC4_PRESET;
|
|
3951
|
+
this.config = extendPresetWithProfile(basePreset, config.profile);
|
|
3949
3952
|
this.mixWeights = { ...this.config.auMixDefaults };
|
|
3950
3953
|
this.animation = animation || new AnimationThree();
|
|
3951
3954
|
this.compositeRotations = this.config.compositeRotations || COMPOSITE_ROTATIONS;
|
|
@@ -5354,7 +5357,30 @@ var BLENDING_MODES = {
|
|
|
5354
5357
|
// THREE.NoBlending
|
|
5355
5358
|
};
|
|
5356
5359
|
|
|
5357
|
-
// src/
|
|
5360
|
+
// src/regions/normalizeRegionTree.ts
|
|
5361
|
+
function normalizeDisabledNames(disabledNames) {
|
|
5362
|
+
return new Set((disabledNames ?? []).filter((name) => Boolean(name)));
|
|
5363
|
+
}
|
|
5364
|
+
function normalizeRegionTree(regions, disabledNames) {
|
|
5365
|
+
if (!regions) return void 0;
|
|
5366
|
+
const disabled = normalizeDisabledNames(disabledNames);
|
|
5367
|
+
const nextRegions = regions.filter((region) => !disabled.has(region.name)).map((region) => ({
|
|
5368
|
+
...region,
|
|
5369
|
+
children: region.children ? [...region.children] : void 0
|
|
5370
|
+
}));
|
|
5371
|
+
const remainingNames = new Set(nextRegions.map((region) => region.name));
|
|
5372
|
+
return nextRegions.map((region) => {
|
|
5373
|
+
const nextChildren = region.children?.filter((child) => remainingNames.has(child));
|
|
5374
|
+
const nextParent = region.parent && remainingNames.has(region.parent) ? region.parent : void 0;
|
|
5375
|
+
return {
|
|
5376
|
+
...region,
|
|
5377
|
+
parent: nextParent,
|
|
5378
|
+
children: nextChildren && nextChildren.length > 0 ? nextChildren : void 0
|
|
5379
|
+
};
|
|
5380
|
+
});
|
|
5381
|
+
}
|
|
5382
|
+
|
|
5383
|
+
// src/characters/extendCharacterConfigWithPreset.ts
|
|
5358
5384
|
var PROFILE_OVERRIDE_KEYS = [
|
|
5359
5385
|
"name",
|
|
5360
5386
|
"animalType",
|
|
@@ -5382,6 +5408,7 @@ var PROFILE_OVERRIDE_KEYS = [
|
|
|
5382
5408
|
"continuumPairs",
|
|
5383
5409
|
"continuumLabels",
|
|
5384
5410
|
"annotationRegions",
|
|
5411
|
+
"disabledRegions",
|
|
5385
5412
|
"hairPhysics"
|
|
5386
5413
|
];
|
|
5387
5414
|
function isPlainObject2(value) {
|
|
@@ -5500,11 +5527,11 @@ function applyCharacterProfileToPreset(config) {
|
|
|
5500
5527
|
if (!presetType) {
|
|
5501
5528
|
return null;
|
|
5502
5529
|
}
|
|
5503
|
-
return
|
|
5530
|
+
return extendPresetWithProfile(getPreset(presetType), extractProfileOverrides(config));
|
|
5504
5531
|
}
|
|
5505
|
-
function
|
|
5506
|
-
if (!
|
|
5507
|
-
const
|
|
5532
|
+
function orderExtendedRegions(extendedRegions, prioritizedLists) {
|
|
5533
|
+
if (!extendedRegions) return void 0;
|
|
5534
|
+
const extendedByName = new Map(extendedRegions.map((region) => [region.name, region]));
|
|
5508
5535
|
const orderedNames = [];
|
|
5509
5536
|
const seen = /* @__PURE__ */ new Set();
|
|
5510
5537
|
for (const regions of prioritizedLists) {
|
|
@@ -5514,12 +5541,12 @@ function orderResolvedRegions(resolvedRegions, prioritizedLists) {
|
|
|
5514
5541
|
orderedNames.push(region.name);
|
|
5515
5542
|
}
|
|
5516
5543
|
}
|
|
5517
|
-
for (const region of
|
|
5544
|
+
for (const region of extendedRegions) {
|
|
5518
5545
|
if (seen.has(region.name)) continue;
|
|
5519
5546
|
seen.add(region.name);
|
|
5520
5547
|
orderedNames.push(region.name);
|
|
5521
5548
|
}
|
|
5522
|
-
return orderedNames.map((name) =>
|
|
5549
|
+
return orderedNames.map((name) => extendedByName.get(name)).filter((region) => Boolean(region));
|
|
5523
5550
|
}
|
|
5524
5551
|
function extendCharacterConfigWithPreset(config) {
|
|
5525
5552
|
const presetType = config.auPresetType;
|
|
@@ -5527,25 +5554,26 @@ function extendCharacterConfigWithPreset(config) {
|
|
|
5527
5554
|
return config;
|
|
5528
5555
|
}
|
|
5529
5556
|
const profileOverrides = extractProfileOverrides(config);
|
|
5530
|
-
const
|
|
5531
|
-
if (!
|
|
5557
|
+
const extendedPresetProfile = applyCharacterProfileToPreset(config);
|
|
5558
|
+
if (!extendedPresetProfile) {
|
|
5532
5559
|
return config;
|
|
5533
5560
|
}
|
|
5534
|
-
const presetRegions =
|
|
5561
|
+
const presetRegions = extendedPresetProfile.annotationRegions;
|
|
5535
5562
|
const mergedRegions = mergeRegionsByName(presetRegions, config.regions);
|
|
5536
|
-
const
|
|
5563
|
+
const normalizedRegions = normalizeRegionTree(
|
|
5537
5564
|
mergedRegions,
|
|
5565
|
+
profileOverrides.disabledRegions
|
|
5566
|
+
);
|
|
5567
|
+
const extendedRegions = orderExtendedRegions(
|
|
5568
|
+
normalizedRegions,
|
|
5538
5569
|
[config.regions, profileOverrides.annotationRegions, presetRegions]
|
|
5539
5570
|
);
|
|
5540
5571
|
return {
|
|
5541
5572
|
...config,
|
|
5542
|
-
...
|
|
5543
|
-
regions:
|
|
5573
|
+
...extendedPresetProfile,
|
|
5574
|
+
regions: extendedRegions ?? config.regions
|
|
5544
5575
|
};
|
|
5545
5576
|
}
|
|
5546
|
-
function resolveCharacterConfig(config) {
|
|
5547
|
-
return extendCharacterConfigWithPreset(config);
|
|
5548
|
-
}
|
|
5549
5577
|
var DEFAULT_HEAD_BONE_NAMES = ["CC_Base_Head", "Head", "head", "Bip01_Head"];
|
|
5550
5578
|
var DEFAULT_REFERENCE_HEIGHT = 1.8;
|
|
5551
5579
|
var DEFAULT_FORWARD_OFFSET = 0.08;
|
|
@@ -6892,10 +6920,10 @@ exports.VISEME_JAW_AMOUNTS = VISEME_JAW_AMOUNTS;
|
|
|
6892
6920
|
exports.VISEME_KEYS = VISEME_KEYS;
|
|
6893
6921
|
exports.analyzeModel = analyzeModel;
|
|
6894
6922
|
exports.applyCharacterProfileToPreset = applyCharacterProfileToPreset;
|
|
6895
|
-
exports.applyProfileToPreset = applyProfileToPreset;
|
|
6896
6923
|
exports.collectMorphMeshes = collectMorphMeshes;
|
|
6897
6924
|
exports.detectFacingDirection = detectFacingDirection;
|
|
6898
6925
|
exports.extendCharacterConfigWithPreset = extendCharacterConfigWithPreset;
|
|
6926
|
+
exports.extendPresetWithProfile = extendPresetWithProfile;
|
|
6899
6927
|
exports.extractFromGLTF = extractFromGLTF;
|
|
6900
6928
|
exports.extractModelData = extractModelData;
|
|
6901
6929
|
exports.extractProfileOverrides = extractProfileOverrides;
|
|
@@ -6903,17 +6931,15 @@ exports.findFaceCenter = findFaceCenter;
|
|
|
6903
6931
|
exports.fuzzyNameMatch = fuzzyNameMatch;
|
|
6904
6932
|
exports.generateMappingCorrections = generateMappingCorrections;
|
|
6905
6933
|
exports.getModelForwardDirection = getModelForwardDirection;
|
|
6934
|
+
exports.getPreset = getPreset;
|
|
6935
|
+
exports.getPresetWithProfile = getPresetWithProfile;
|
|
6906
6936
|
exports.hasLeftRightMorphs = hasLeftRightMorphs;
|
|
6907
6937
|
exports.isMixedAU = isMixedAU;
|
|
6908
6938
|
exports.isPresetCompatible = isPresetCompatible;
|
|
6909
6939
|
exports.mergeCharacterRegionsByName = mergeRegionsByName;
|
|
6910
6940
|
exports.resolveBoneName = resolveBoneName;
|
|
6911
6941
|
exports.resolveBoneNames = resolveBoneNames;
|
|
6912
|
-
exports.resolveCharacterConfig = resolveCharacterConfig;
|
|
6913
6942
|
exports.resolveFaceCenter = resolveFaceCenter;
|
|
6914
|
-
exports.resolvePreset = resolvePreset;
|
|
6915
|
-
exports.resolvePresetWithOverrides = resolvePresetWithOverrides;
|
|
6916
|
-
exports.resolveProfile = resolveProfile;
|
|
6917
6943
|
exports.suggestBestPreset = suggestBestPreset;
|
|
6918
6944
|
exports.validateMappingConfig = validateMappingConfig;
|
|
6919
6945
|
exports.validateMappings = validateMappings;
|