@lovelace_lol/loom3 1.0.12 → 1.0.13
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 +113 -42
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ Loom3 provides mappings that connect [Facial Action Coding System (FACS)](https:
|
|
|
27
27
|
13. [Playback & State Control](#13-playback--state-control)
|
|
28
28
|
14. [Hair Physics](#14-hair-physics)
|
|
29
29
|
15. [Baked Animations](#15-baked-animations)
|
|
30
|
+
16. [API Reference](#16-api-reference)
|
|
30
31
|
|
|
31
32
|
---
|
|
32
33
|
|
|
@@ -81,7 +82,7 @@ loader.load('/character.glb', (gltf) => {
|
|
|
81
82
|
// This drives all transitions and animations
|
|
82
83
|
```
|
|
83
84
|
|
|
84
|
-
If you’re implementing a custom renderer, target the `
|
|
85
|
+
If you’re implementing a custom renderer, target the `Loom3` interface exported from `@lovelace_lol/loom3` (legacy alias: `LoomLarge`).
|
|
85
86
|
|
|
86
87
|
### Quick start examples
|
|
87
88
|
|
|
@@ -162,26 +163,31 @@ import { CC4_PRESET } from '@lovelace_lol/loom3';
|
|
|
162
163
|
},
|
|
163
164
|
|
|
164
165
|
boneNodes: {
|
|
165
|
-
// Logical bone name →
|
|
166
|
-
'HEAD': '
|
|
167
|
-
'JAW': '
|
|
168
|
-
'EYE_L': '
|
|
169
|
-
'EYE_R': '
|
|
170
|
-
'TONGUE': '
|
|
166
|
+
// Logical bone name → base node name used with bonePrefix
|
|
167
|
+
'HEAD': 'Head',
|
|
168
|
+
'JAW': 'JawRoot',
|
|
169
|
+
'EYE_L': 'L_Eye',
|
|
170
|
+
'EYE_R': 'R_Eye',
|
|
171
|
+
'TONGUE': 'Tongue01',
|
|
171
172
|
},
|
|
172
173
|
|
|
174
|
+
bonePrefix: 'CC_Base_',
|
|
175
|
+
suffixPattern: '_\\d+$|\\.\\d+$',
|
|
176
|
+
|
|
173
177
|
visemeKeys: [
|
|
174
178
|
// 15 viseme morph names for lip-sync
|
|
175
|
-
'
|
|
176
|
-
'
|
|
177
|
-
'
|
|
179
|
+
'EE', 'Ah', 'Oh', 'OO', 'I',
|
|
180
|
+
'U', 'W', 'L', 'F_V', 'Th',
|
|
181
|
+
'S_Z', 'B_M_P', 'K_G_H_NG', 'AE', 'R'
|
|
178
182
|
],
|
|
179
183
|
|
|
180
184
|
morphToMesh: {
|
|
181
185
|
// Routes morph categories to specific meshes
|
|
182
186
|
'face': ['CC_Base_Body'],
|
|
187
|
+
'viseme': ['CC_Base_Body', 'CC_Base_Body_1'],
|
|
183
188
|
'tongue': ['CC_Base_Tongue'],
|
|
184
|
-
'eye': ['
|
|
189
|
+
'eye': ['CC_Base_EyeOcclusion_1', 'CC_Base_EyeOcclusion_2'],
|
|
190
|
+
'hair': ['Side_part_wavy_1', 'Side_part_wavy_2'],
|
|
185
191
|
},
|
|
186
192
|
|
|
187
193
|
auMixDefaults: {
|
|
@@ -203,6 +209,25 @@ import { CC4_PRESET } from '@lovelace_lol/loom3';
|
|
|
203
209
|
}
|
|
204
210
|
```
|
|
205
211
|
|
|
212
|
+
### Name resolution and profile fields
|
|
213
|
+
|
|
214
|
+
The runtime resolves bone nodes by composing `bonePrefix + boneNodes[key] + boneSuffix`, then falling back to suffix-pattern matching when a model uses numbered exports such as `_01` or `.001`. The same prefix/suffix rules are used by validation and correction helpers, which is why `CC4_PRESET` can keep base bone names like `Head` and `JawRoot` instead of repeating the full node names everywhere.
|
|
215
|
+
|
|
216
|
+
For region and marker configs, `resolveBoneName()` treats any mapped bone name that already contains `_` or `.` as a fully qualified name and skips prefix/suffix composition.
|
|
217
|
+
|
|
218
|
+
Two caveats are worth calling out:
|
|
219
|
+
- `morphPrefix` and `morphSuffix` are part of `Profile`, but morph playback still resolves exact morph keys on the targeted meshes today. They are already used by validation and correction helpers.
|
|
220
|
+
- `leftMorphSuffixes` and `rightMorphSuffixes` are profile metadata for laterality detection in tooling, not core runtime behavior.
|
|
221
|
+
|
|
222
|
+
Other `Profile` fields that are easy to miss:
|
|
223
|
+
- `morphToMesh` routes categories such as `face`, `viseme`, `eye`, `tongue`, and `hair` to specific mesh names.
|
|
224
|
+
- `eyeMeshNodes` provides fallback eye nodes when a rig uses meshes instead of bones for eye control.
|
|
225
|
+
- `auMixDefaults` sets the default morph/bone blend weight per AU.
|
|
226
|
+
- `compositeRotations` defines the per-node pitch/yaw/roll axis layout used by the composite rotation system.
|
|
227
|
+
- `continuumPairs` and `continuumLabels` describe bidirectional AU pairs and their UI labels.
|
|
228
|
+
- `annotationRegions` defines the regions used by the marker and camera tooling.
|
|
229
|
+
- `hairPhysics` stores the mixer-driven hair defaults, including direction signs and morph target mappings.
|
|
230
|
+
|
|
206
231
|
### Passing a preset to Loom3
|
|
207
232
|
|
|
208
233
|
```typescript
|
|
@@ -319,6 +344,7 @@ Loom3 includes validation and analysis helpers so you can verify presets against
|
|
|
319
344
|
|
|
320
345
|
```typescript
|
|
321
346
|
import {
|
|
347
|
+
extractFromGLTF,
|
|
322
348
|
extractModelData,
|
|
323
349
|
analyzeModel,
|
|
324
350
|
validateMappings,
|
|
@@ -327,16 +353,41 @@ import {
|
|
|
327
353
|
} from '@lovelace_lol/loom3';
|
|
328
354
|
|
|
329
355
|
const preset = resolvePreset('cc4');
|
|
330
|
-
const modelData = extractModelData(
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
const
|
|
356
|
+
const modelData = extractModelData(model, meshes, animations);
|
|
357
|
+
const gltfData = extractFromGLTF(gltf); // Same ModelData shape, one-step GLTF wrapper
|
|
358
|
+
|
|
359
|
+
const analysis = await analyzeModel({
|
|
360
|
+
source: { type: 'gltf', gltf },
|
|
361
|
+
preset,
|
|
362
|
+
suggestCorrections: true,
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Validate against lower-level mesh + skeleton inputs when you already have them
|
|
366
|
+
const validation = validateMappings(meshes, skeleton, preset, { suggestCorrections: true });
|
|
367
|
+
const corrections = generateMappingCorrections(meshes, skeleton, preset, { useResolvedNames: true });
|
|
334
368
|
```
|
|
335
369
|
|
|
370
|
+
If you already have a `ModelData` bundle, `analyzeModel()` is the higher-level path; `validateMappings()` and `generateMappingCorrections()` are intentionally lower-level mesh/skeleton helpers.
|
|
371
|
+
|
|
336
372
|
Use these helpers to:
|
|
337
|
-
-
|
|
338
|
-
-
|
|
339
|
-
-
|
|
373
|
+
- Extract raw model facts with `extractModelData(model, meshes?, animations?)` or `extractFromGLTF(gltf)`
|
|
374
|
+
- Validate a preset against mesh/skeleton data with `validateMappings(meshes, skeleton, preset, options)`
|
|
375
|
+
- Generate best-effort fixes with `generateMappingCorrections(meshes, skeleton, preset, options)`
|
|
376
|
+
- Run a single end-to-end pass with `analyzeModel({ source, preset, suggestCorrections })`
|
|
377
|
+
|
|
378
|
+
`validateMappings()` returns a `ValidationResult` with:
|
|
379
|
+
- `valid` and `score`
|
|
380
|
+
- `missingMorphs`, `missingBones`, `foundMorphs`, `foundBones`
|
|
381
|
+
- `missingMeshes`, `foundMeshes`, `unmappedMorphs`, `unmappedBones`, `unmappedMeshes`
|
|
382
|
+
- `warnings`
|
|
383
|
+
- optional `suggestedConfig`, `corrections`, and `unresolved` when suggestion mode is enabled
|
|
384
|
+
|
|
385
|
+
`generateMappingCorrections()` returns:
|
|
386
|
+
- `correctedConfig`
|
|
387
|
+
- `corrections`
|
|
388
|
+
- `unresolved`
|
|
389
|
+
|
|
390
|
+
`analyzeModel()` returns a `ModelAnalysisReport` containing the extracted model data, optional validation results, animation summary, `overallScore`, and a plain-language `summary`.
|
|
340
391
|
|
|
341
392
|
### Controlling mesh visibility
|
|
342
393
|
|
|
@@ -387,16 +438,16 @@ This is especially useful for:
|
|
|
387
438
|
|
|
388
439
|
## 4. Extending & Custom Presets
|
|
389
440
|
|
|
390
|
-

|
|
391
442
|
|
|
392
443
|
### Extending an existing preset
|
|
393
444
|
|
|
394
|
-
Use `
|
|
445
|
+
Use `resolveProfile` to override specific mappings while keeping the rest:
|
|
395
446
|
|
|
396
447
|
```typescript
|
|
397
|
-
import { CC4_PRESET,
|
|
448
|
+
import { CC4_PRESET, resolveProfile } from '@lovelace_lol/loom3';
|
|
398
449
|
|
|
399
|
-
const MY_PRESET =
|
|
450
|
+
const MY_PRESET = resolveProfile(CC4_PRESET, {
|
|
400
451
|
|
|
401
452
|
// Override AU12 (smile) with custom morph names
|
|
402
453
|
auToMorphs: {
|
|
@@ -420,7 +471,7 @@ const loom = new Loom3({ profile: MY_PRESET });
|
|
|
420
471
|
### Creating a preset from scratch
|
|
421
472
|
|
|
422
473
|
```typescript
|
|
423
|
-
import { Profile } from '@lovelace_lol/loom3';
|
|
474
|
+
import type { Profile } from '@lovelace_lol/loom3';
|
|
424
475
|
|
|
425
476
|
const CUSTOM_PRESET: Profile = {
|
|
426
477
|
auToMorphs: {
|
|
@@ -1075,19 +1126,21 @@ Visemes are mouth shapes used for lip-sync. Loom3 includes 15 visemes with autom
|
|
|
1075
1126
|
|
|
1076
1127
|
### The 15 visemes
|
|
1077
1128
|
|
|
1129
|
+
The `VISEME_KEYS` export uses unprefixed keys in this order.
|
|
1130
|
+
|
|
1078
1131
|
| Index | Key | Phoneme Example |
|
|
1079
1132
|
|-------|-----|-----------------|
|
|
1080
1133
|
| 0 | EE | "b**ee**" |
|
|
1081
|
-
| 1 |
|
|
1082
|
-
| 2 |
|
|
1083
|
-
| 3 |
|
|
1084
|
-
| 4 |
|
|
1085
|
-
| 5 |
|
|
1086
|
-
| 6 |
|
|
1087
|
-
| 7 |
|
|
1134
|
+
| 1 | Ah | "f**a**ther" |
|
|
1135
|
+
| 2 | Oh | "g**o**" |
|
|
1136
|
+
| 3 | OO | "t**oo**" |
|
|
1137
|
+
| 4 | I | "s**i**t" |
|
|
1138
|
+
| 5 | U | "fl**u**te" |
|
|
1139
|
+
| 6 | W | "**w**e" |
|
|
1140
|
+
| 7 | L | "**l**ip" |
|
|
1088
1141
|
| 8 | F_V | "**f**un, **v**an" |
|
|
1089
|
-
| 9 |
|
|
1090
|
-
| 10 |
|
|
1142
|
+
| 9 | Th | "**th**ink" |
|
|
1143
|
+
| 10 | S_Z | "**s**un, **z**oo" |
|
|
1091
1144
|
| 11 | B_M_P | "**b**at, **m**an, **p**op" |
|
|
1092
1145
|
| 12 | K_G_H_NG | "**k**ite, **g**o, **h**at, si**ng**" |
|
|
1093
1146
|
| 13 | AE | "c**a**t" |
|
|
@@ -1117,16 +1170,7 @@ loom.transitionViseme(3, 1.0, 80, 0);
|
|
|
1117
1170
|
|
|
1118
1171
|
### Automatic jaw coupling
|
|
1119
1172
|
|
|
1120
|
-
Each viseme has a predefined jaw opening amount. When you set a viseme, the jaw automatically opens proportionally:
|
|
1121
|
-
|
|
1122
|
-
| Viseme | Jaw Amount |
|
|
1123
|
-
|--------|------------|
|
|
1124
|
-
| EE | 0.15 |
|
|
1125
|
-
| Ah | 0.70 |
|
|
1126
|
-
| Oh | 0.50 |
|
|
1127
|
-
| B_M_P | 0.20 |
|
|
1128
|
-
|
|
1129
|
-
The `jawScale` parameter multiplies this amount:
|
|
1173
|
+
Each viseme has a predefined jaw opening amount in the preset. When you set a viseme, the jaw automatically opens proportionally, and the `jawScale` parameter multiplies that amount:
|
|
1130
1174
|
- `jawScale = 1.0`: Normal jaw opening
|
|
1131
1175
|
- `jawScale = 0.5`: Half jaw opening
|
|
1132
1176
|
- `jawScale = 0`: No jaw movement (viseme only)
|
|
@@ -1653,6 +1697,33 @@ loom.transitionAU(45, 1.0, 100); // Blink
|
|
|
1653
1697
|
|
|
1654
1698
|
---
|
|
1655
1699
|
|
|
1700
|
+
## 16. API Reference
|
|
1701
|
+
|
|
1702
|
+
This is a compact reference for the public surface exported by `@lovelace_lol/loom3`.
|
|
1703
|
+
|
|
1704
|
+
### Engine surface
|
|
1705
|
+
|
|
1706
|
+
`Loom3` (legacy alias: `LoomLarge`) covers:
|
|
1707
|
+
- Initialization and lifecycle: `onReady()`, `update()`, `start()`, `stop()`, `dispose()`
|
|
1708
|
+
- AU, morph, viseme, and continuum control: `setAU()`, `transitionAU()`, `setMorph()`, `transitionMorph()`, `setViseme()`, `transitionViseme()`, `setContinuum()`, `transitionContinuum()`
|
|
1709
|
+
- Preset state: `setProfile()`, `getProfile()`
|
|
1710
|
+
- Validation and analysis helpers: `validateMappings()`, `generateMappingCorrections()`, `extractModelData()`, `extractFromGLTF()`, `analyzeModel()`
|
|
1711
|
+
- Mixer and baked animation helpers: `loadAnimationClips()`, `getAnimationClips()`, `playAnimation()`, `playClip()`, `playSnippet()`, `snippetToClip()`
|
|
1712
|
+
- Hair physics helpers: `setHairPhysicsEnabled()`, `setHairPhysicsConfig()`, `validateHairMorphTargets()`
|
|
1713
|
+
|
|
1714
|
+
### Core types and helpers
|
|
1715
|
+
|
|
1716
|
+
- `Profile` groups the mapping tables, name-resolution fields, `morphToMesh`, `visemeKeys`, `auMixDefaults`, `compositeRotations`, `continuumPairs`, `continuumLabels`, `eyeMeshNodes`, `annotationRegions`, and `hairPhysics`.
|
|
1717
|
+
- `TransitionHandle` exposes `promise`, `pause()`, `resume()`, and `cancel()`.
|
|
1718
|
+
- `ClipHandle` exposes `clipName`, `play()`, `stop()`, `pause()`, `resume()`, optional live-update setters, `getTime()`, `getDuration()`, and `finished`.
|
|
1719
|
+
- `collectMorphMeshes()` gathers meshes that already have morph targets.
|
|
1720
|
+
- `resolvePreset()` picks a built-in preset by name.
|
|
1721
|
+
- `resolvePresetWithOverrides()` resolves a preset and merges partial overrides.
|
|
1722
|
+
- `resolveProfile()` merges a base preset with a partial profile override.
|
|
1723
|
+
- `isMixedAU()` checks whether an AU has both morph and bone support in the CC4 preset.
|
|
1724
|
+
|
|
1725
|
+
---
|
|
1726
|
+
|
|
1656
1727
|
## Resources
|
|
1657
1728
|
|
|
1658
1729
|

|
package/package.json
CHANGED