loomlarge 0.1.4 → 0.1.6
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 +179 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -2
- package/dist/index.d.ts +84 -2
- package/dist/index.js +166 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -35,7 +35,7 @@ interface AUMappingConfig {
|
|
|
35
35
|
/**
|
|
36
36
|
* Helper type for mesh categories in morphToMesh
|
|
37
37
|
*/
|
|
38
|
-
type MorphCategory = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
|
|
38
|
+
type MorphCategory$1 = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* LoomLarge - Core Type Definitions
|
|
@@ -203,10 +203,21 @@ interface LoomLarge {
|
|
|
203
203
|
onReady(payload: ReadyPayload): void;
|
|
204
204
|
/**
|
|
205
205
|
* Update animation state. Call each frame with delta time in seconds.
|
|
206
|
+
* If using start(), this is called automatically.
|
|
206
207
|
*/
|
|
207
208
|
update(deltaSeconds: number): void;
|
|
209
|
+
/**
|
|
210
|
+
* Start the internal animation loop (RAF-based).
|
|
211
|
+
* Automatically calls update() each frame with delta time.
|
|
212
|
+
*/
|
|
213
|
+
start(): void;
|
|
214
|
+
/**
|
|
215
|
+
* Stop the internal animation loop.
|
|
216
|
+
*/
|
|
217
|
+
stop(): void;
|
|
208
218
|
/**
|
|
209
219
|
* Dispose engine resources and cleanup.
|
|
220
|
+
* Stops the animation loop and clears all transitions.
|
|
210
221
|
*/
|
|
211
222
|
dispose(): void;
|
|
212
223
|
/**
|
|
@@ -369,11 +380,18 @@ declare class LoomLargeThree implements LoomLarge {
|
|
|
369
380
|
private bones;
|
|
370
381
|
private mixWeights;
|
|
371
382
|
private visemeValues;
|
|
383
|
+
private clock;
|
|
384
|
+
private rafId;
|
|
385
|
+
private running;
|
|
372
386
|
private static readonly VISEME_JAW_AMOUNTS;
|
|
373
387
|
private static readonly JAW_MAX_DEGREES;
|
|
374
388
|
constructor(config?: LoomLargeConfig, animation?: Animation);
|
|
375
389
|
onReady(payload: ReadyPayload): void;
|
|
376
390
|
update(deltaSeconds: number): void;
|
|
391
|
+
/** Start the internal RAF loop */
|
|
392
|
+
start(): void;
|
|
393
|
+
/** Stop the internal RAF loop */
|
|
394
|
+
stop(): void;
|
|
377
395
|
dispose(): void;
|
|
378
396
|
setAU(id: number | string, v: number, balance?: number): void;
|
|
379
397
|
transitionAU(id: number | string, to: number, durationMs?: number, balance?: number): TransitionHandle;
|
|
@@ -539,6 +557,70 @@ interface HairPhysics$1 {
|
|
|
539
557
|
* and AU metadata that we painstakingly worked through.
|
|
540
558
|
*/
|
|
541
559
|
|
|
560
|
+
declare const AU_TO_MORPHS: Record<number, string[]>;
|
|
561
|
+
declare const VISEME_KEYS: string[];
|
|
562
|
+
declare const BONE_AU_TO_BINDINGS: Record<number, BoneBinding[]>;
|
|
563
|
+
/** Check if an AU has both morphs and bones (can blend between them) */
|
|
564
|
+
declare const isMixedAU: (id: number) => boolean;
|
|
565
|
+
/** Check if an AU has separate left/right morphs */
|
|
566
|
+
declare const hasLeftRightMorphs: (auId: number) => boolean;
|
|
567
|
+
declare const COMPOSITE_ROTATIONS: CompositeRotation[];
|
|
568
|
+
/**
|
|
569
|
+
* Continuum pair mappings - precomputed from COMPOSITE_ROTATIONS
|
|
570
|
+
* Maps AU ID to its continuum partner info for bidirectional axes
|
|
571
|
+
* (e.g., AU 51 "Head Left" is paired with AU 52 "Head Right")
|
|
572
|
+
*/
|
|
573
|
+
declare const CONTINUUM_PAIRS_MAP: Record<number, {
|
|
574
|
+
pairId: number;
|
|
575
|
+
isNegative: boolean;
|
|
576
|
+
axis: 'pitch' | 'yaw' | 'roll';
|
|
577
|
+
node: 'JAW' | 'HEAD' | 'EYE_L' | 'EYE_R' | 'TONGUE';
|
|
578
|
+
}>;
|
|
579
|
+
/**
|
|
580
|
+
* Human-readable labels for continuum pairs
|
|
581
|
+
* Key format: "negativeAU-positiveAU"
|
|
582
|
+
* Used by UI components (ContinuumSlider, AUSection) to display friendly axis names
|
|
583
|
+
*/
|
|
584
|
+
declare const CONTINUUM_LABELS: Record<string, string>;
|
|
585
|
+
declare const CC4_BONE_NODES: {
|
|
586
|
+
readonly EYE_L: "CC_Base_L_Eye";
|
|
587
|
+
readonly EYE_R: "CC_Base_R_Eye";
|
|
588
|
+
readonly HEAD: "CC_Base_Head";
|
|
589
|
+
readonly NECK: "CC_Base_NeckTwist01";
|
|
590
|
+
readonly NECK_TWIST: "CC_Base_NeckTwist02";
|
|
591
|
+
readonly JAW: "CC_Base_JawRoot";
|
|
592
|
+
readonly TONGUE: "CC_Base_Tongue01";
|
|
593
|
+
};
|
|
594
|
+
declare const CC4_EYE_MESH_NODES: {
|
|
595
|
+
readonly LEFT: "CC_Base_Eye";
|
|
596
|
+
readonly RIGHT: "CC_Base_Eye_1";
|
|
597
|
+
};
|
|
598
|
+
declare const AU_INFO: Record<string, AUInfo>;
|
|
599
|
+
/** Default mix weights (0 = morph only, 1 = bone only) */
|
|
600
|
+
declare const AU_MIX_DEFAULTS: Record<number, number>;
|
|
601
|
+
type MeshCategory = 'body' | 'eye' | 'eyeOcclusion' | 'tearLine' | 'teeth' | 'tongue' | 'hair' | 'eyebrow' | 'cornea' | 'eyelash';
|
|
602
|
+
/** Blending mode names (matches Three.js constants) */
|
|
603
|
+
type BlendingMode = 'Normal' | 'Additive' | 'Subtractive' | 'Multiply' | 'None';
|
|
604
|
+
/** Material settings for mesh rendering */
|
|
605
|
+
interface MeshMaterialSettings {
|
|
606
|
+
renderOrder?: number;
|
|
607
|
+
transparent?: boolean;
|
|
608
|
+
opacity?: number;
|
|
609
|
+
depthWrite?: boolean;
|
|
610
|
+
depthTest?: boolean;
|
|
611
|
+
blending?: BlendingMode;
|
|
612
|
+
}
|
|
613
|
+
/** Mesh info including category, morph count, and optional material settings */
|
|
614
|
+
interface CC4MeshInfo {
|
|
615
|
+
category: MeshCategory;
|
|
616
|
+
morphCount: number;
|
|
617
|
+
material?: MeshMaterialSettings;
|
|
618
|
+
}
|
|
619
|
+
/** Exact mesh name -> category mapping from the character GLB */
|
|
620
|
+
declare const CC4_MESHES: Record<string, CC4MeshInfo>;
|
|
621
|
+
type MorphCategory = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
|
|
622
|
+
/** Which mesh each morph category applies to */
|
|
623
|
+
declare const MORPH_TO_MESH: Record<MorphCategory, string[]>;
|
|
542
624
|
declare const CC4_PRESET: AUMappingConfig;
|
|
543
625
|
|
|
544
626
|
/**
|
|
@@ -628,4 +710,4 @@ declare class HairPhysics {
|
|
|
628
710
|
reset(): void;
|
|
629
711
|
}
|
|
630
712
|
|
|
631
|
-
export { type AUInfo, type AUMappingConfig, type Animation, AnimationThree, type BoneBinding, type BoneKey, CC4_PRESET, type CompositeRotation, type CompositeRotationState, DEFAULT_HAIR_PHYSICS_CONFIG, type HairMorphOutput$1 as HairMorphOutput, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LoomEuler, type LoomLarge, type LoomLargeConfig, LoomLargeThree, type LoomMesh, type LoomObject3D, type LoomQuaternion, type LoomVector3, type MeshInfo, type MorphCategory, type ReadyPayload, type RotationAxis, type RotationAxisState, type RotationsState, type TransitionHandle, collectMorphMeshes, LoomLargeThree as default };
|
|
713
|
+
export { type AUInfo, type AUMappingConfig, AU_INFO, AU_MIX_DEFAULTS, AU_TO_MORPHS, type Animation, AnimationThree, BONE_AU_TO_BINDINGS, type BoneBinding, type BoneKey, CC4_BONE_NODES, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, type CompositeRotation, type CompositeRotationState, DEFAULT_HAIR_PHYSICS_CONFIG, type HairMorphOutput$1 as HairMorphOutput, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LoomEuler, type LoomLarge, type LoomLargeConfig, LoomLargeThree, type LoomMesh, type LoomObject3D, type LoomQuaternion, type LoomVector3, MORPH_TO_MESH, type MeshInfo, type MorphCategory$1 as MorphCategory, type ReadyPayload, type RotationAxis, type RotationAxisState, type RotationsState, type TransitionHandle, VISEME_KEYS, collectMorphMeshes, LoomLargeThree as default, hasLeftRightMorphs, isMixedAU };
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ interface AUMappingConfig {
|
|
|
35
35
|
/**
|
|
36
36
|
* Helper type for mesh categories in morphToMesh
|
|
37
37
|
*/
|
|
38
|
-
type MorphCategory = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
|
|
38
|
+
type MorphCategory$1 = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* LoomLarge - Core Type Definitions
|
|
@@ -203,10 +203,21 @@ interface LoomLarge {
|
|
|
203
203
|
onReady(payload: ReadyPayload): void;
|
|
204
204
|
/**
|
|
205
205
|
* Update animation state. Call each frame with delta time in seconds.
|
|
206
|
+
* If using start(), this is called automatically.
|
|
206
207
|
*/
|
|
207
208
|
update(deltaSeconds: number): void;
|
|
209
|
+
/**
|
|
210
|
+
* Start the internal animation loop (RAF-based).
|
|
211
|
+
* Automatically calls update() each frame with delta time.
|
|
212
|
+
*/
|
|
213
|
+
start(): void;
|
|
214
|
+
/**
|
|
215
|
+
* Stop the internal animation loop.
|
|
216
|
+
*/
|
|
217
|
+
stop(): void;
|
|
208
218
|
/**
|
|
209
219
|
* Dispose engine resources and cleanup.
|
|
220
|
+
* Stops the animation loop and clears all transitions.
|
|
210
221
|
*/
|
|
211
222
|
dispose(): void;
|
|
212
223
|
/**
|
|
@@ -369,11 +380,18 @@ declare class LoomLargeThree implements LoomLarge {
|
|
|
369
380
|
private bones;
|
|
370
381
|
private mixWeights;
|
|
371
382
|
private visemeValues;
|
|
383
|
+
private clock;
|
|
384
|
+
private rafId;
|
|
385
|
+
private running;
|
|
372
386
|
private static readonly VISEME_JAW_AMOUNTS;
|
|
373
387
|
private static readonly JAW_MAX_DEGREES;
|
|
374
388
|
constructor(config?: LoomLargeConfig, animation?: Animation);
|
|
375
389
|
onReady(payload: ReadyPayload): void;
|
|
376
390
|
update(deltaSeconds: number): void;
|
|
391
|
+
/** Start the internal RAF loop */
|
|
392
|
+
start(): void;
|
|
393
|
+
/** Stop the internal RAF loop */
|
|
394
|
+
stop(): void;
|
|
377
395
|
dispose(): void;
|
|
378
396
|
setAU(id: number | string, v: number, balance?: number): void;
|
|
379
397
|
transitionAU(id: number | string, to: number, durationMs?: number, balance?: number): TransitionHandle;
|
|
@@ -539,6 +557,70 @@ interface HairPhysics$1 {
|
|
|
539
557
|
* and AU metadata that we painstakingly worked through.
|
|
540
558
|
*/
|
|
541
559
|
|
|
560
|
+
declare const AU_TO_MORPHS: Record<number, string[]>;
|
|
561
|
+
declare const VISEME_KEYS: string[];
|
|
562
|
+
declare const BONE_AU_TO_BINDINGS: Record<number, BoneBinding[]>;
|
|
563
|
+
/** Check if an AU has both morphs and bones (can blend between them) */
|
|
564
|
+
declare const isMixedAU: (id: number) => boolean;
|
|
565
|
+
/** Check if an AU has separate left/right morphs */
|
|
566
|
+
declare const hasLeftRightMorphs: (auId: number) => boolean;
|
|
567
|
+
declare const COMPOSITE_ROTATIONS: CompositeRotation[];
|
|
568
|
+
/**
|
|
569
|
+
* Continuum pair mappings - precomputed from COMPOSITE_ROTATIONS
|
|
570
|
+
* Maps AU ID to its continuum partner info for bidirectional axes
|
|
571
|
+
* (e.g., AU 51 "Head Left" is paired with AU 52 "Head Right")
|
|
572
|
+
*/
|
|
573
|
+
declare const CONTINUUM_PAIRS_MAP: Record<number, {
|
|
574
|
+
pairId: number;
|
|
575
|
+
isNegative: boolean;
|
|
576
|
+
axis: 'pitch' | 'yaw' | 'roll';
|
|
577
|
+
node: 'JAW' | 'HEAD' | 'EYE_L' | 'EYE_R' | 'TONGUE';
|
|
578
|
+
}>;
|
|
579
|
+
/**
|
|
580
|
+
* Human-readable labels for continuum pairs
|
|
581
|
+
* Key format: "negativeAU-positiveAU"
|
|
582
|
+
* Used by UI components (ContinuumSlider, AUSection) to display friendly axis names
|
|
583
|
+
*/
|
|
584
|
+
declare const CONTINUUM_LABELS: Record<string, string>;
|
|
585
|
+
declare const CC4_BONE_NODES: {
|
|
586
|
+
readonly EYE_L: "CC_Base_L_Eye";
|
|
587
|
+
readonly EYE_R: "CC_Base_R_Eye";
|
|
588
|
+
readonly HEAD: "CC_Base_Head";
|
|
589
|
+
readonly NECK: "CC_Base_NeckTwist01";
|
|
590
|
+
readonly NECK_TWIST: "CC_Base_NeckTwist02";
|
|
591
|
+
readonly JAW: "CC_Base_JawRoot";
|
|
592
|
+
readonly TONGUE: "CC_Base_Tongue01";
|
|
593
|
+
};
|
|
594
|
+
declare const CC4_EYE_MESH_NODES: {
|
|
595
|
+
readonly LEFT: "CC_Base_Eye";
|
|
596
|
+
readonly RIGHT: "CC_Base_Eye_1";
|
|
597
|
+
};
|
|
598
|
+
declare const AU_INFO: Record<string, AUInfo>;
|
|
599
|
+
/** Default mix weights (0 = morph only, 1 = bone only) */
|
|
600
|
+
declare const AU_MIX_DEFAULTS: Record<number, number>;
|
|
601
|
+
type MeshCategory = 'body' | 'eye' | 'eyeOcclusion' | 'tearLine' | 'teeth' | 'tongue' | 'hair' | 'eyebrow' | 'cornea' | 'eyelash';
|
|
602
|
+
/** Blending mode names (matches Three.js constants) */
|
|
603
|
+
type BlendingMode = 'Normal' | 'Additive' | 'Subtractive' | 'Multiply' | 'None';
|
|
604
|
+
/** Material settings for mesh rendering */
|
|
605
|
+
interface MeshMaterialSettings {
|
|
606
|
+
renderOrder?: number;
|
|
607
|
+
transparent?: boolean;
|
|
608
|
+
opacity?: number;
|
|
609
|
+
depthWrite?: boolean;
|
|
610
|
+
depthTest?: boolean;
|
|
611
|
+
blending?: BlendingMode;
|
|
612
|
+
}
|
|
613
|
+
/** Mesh info including category, morph count, and optional material settings */
|
|
614
|
+
interface CC4MeshInfo {
|
|
615
|
+
category: MeshCategory;
|
|
616
|
+
morphCount: number;
|
|
617
|
+
material?: MeshMaterialSettings;
|
|
618
|
+
}
|
|
619
|
+
/** Exact mesh name -> category mapping from the character GLB */
|
|
620
|
+
declare const CC4_MESHES: Record<string, CC4MeshInfo>;
|
|
621
|
+
type MorphCategory = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
|
|
622
|
+
/** Which mesh each morph category applies to */
|
|
623
|
+
declare const MORPH_TO_MESH: Record<MorphCategory, string[]>;
|
|
542
624
|
declare const CC4_PRESET: AUMappingConfig;
|
|
543
625
|
|
|
544
626
|
/**
|
|
@@ -628,4 +710,4 @@ declare class HairPhysics {
|
|
|
628
710
|
reset(): void;
|
|
629
711
|
}
|
|
630
712
|
|
|
631
|
-
export { type AUInfo, type AUMappingConfig, type Animation, AnimationThree, type BoneBinding, type BoneKey, CC4_PRESET, type CompositeRotation, type CompositeRotationState, DEFAULT_HAIR_PHYSICS_CONFIG, type HairMorphOutput$1 as HairMorphOutput, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LoomEuler, type LoomLarge, type LoomLargeConfig, LoomLargeThree, type LoomMesh, type LoomObject3D, type LoomQuaternion, type LoomVector3, type MeshInfo, type MorphCategory, type ReadyPayload, type RotationAxis, type RotationAxisState, type RotationsState, type TransitionHandle, collectMorphMeshes, LoomLargeThree as default };
|
|
713
|
+
export { type AUInfo, type AUMappingConfig, AU_INFO, AU_MIX_DEFAULTS, AU_TO_MORPHS, type Animation, AnimationThree, BONE_AU_TO_BINDINGS, type BoneBinding, type BoneKey, CC4_BONE_NODES, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, type CompositeRotation, type CompositeRotationState, DEFAULT_HAIR_PHYSICS_CONFIG, type HairMorphOutput$1 as HairMorphOutput, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LoomEuler, type LoomLarge, type LoomLargeConfig, LoomLargeThree, type LoomMesh, type LoomObject3D, type LoomQuaternion, type LoomVector3, MORPH_TO_MESH, type MeshInfo, type MorphCategory$1 as MorphCategory, type ReadyPayload, type RotationAxis, type RotationAxisState, type RotationsState, type TransitionHandle, VISEME_KEYS, collectMorphMeshes, LoomLargeThree as default, hasLeftRightMorphs, isMixedAU };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Clock } from 'three';
|
|
2
|
+
|
|
1
3
|
var __defProp = Object.defineProperty;
|
|
2
4
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
5
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
@@ -354,6 +356,107 @@ var BONE_AU_TO_BINDINGS = {
|
|
|
354
356
|
{ node: "TONGUE", channel: "rx", scale: 1, maxDegrees: 20 }
|
|
355
357
|
]
|
|
356
358
|
};
|
|
359
|
+
var isMixedAU = (id) => !!(AU_TO_MORPHS[id]?.length && BONE_AU_TO_BINDINGS[id]?.length);
|
|
360
|
+
var hasLeftRightMorphs = (auId) => {
|
|
361
|
+
const keys = AU_TO_MORPHS[auId] || [];
|
|
362
|
+
return keys.some((k) => /_L$|_R$| L$| R$|Left$|Right$/i.test(k));
|
|
363
|
+
};
|
|
364
|
+
var COMPOSITE_ROTATIONS = [
|
|
365
|
+
{
|
|
366
|
+
node: "JAW",
|
|
367
|
+
pitch: { aus: [25, 26, 27], axis: "rz" },
|
|
368
|
+
// Jaw drop (opens mouth downward)
|
|
369
|
+
yaw: { aus: [30, 35], axis: "ry", negative: 30, positive: 35 },
|
|
370
|
+
// Jaw lateral (left/right)
|
|
371
|
+
roll: null
|
|
372
|
+
// Jaw doesn't have roll
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
node: "HEAD",
|
|
376
|
+
pitch: { aus: [54, 53], axis: "rx", negative: 54, positive: 53 },
|
|
377
|
+
// Head down/up
|
|
378
|
+
yaw: { aus: [51, 52], axis: "ry", negative: 51, positive: 52 },
|
|
379
|
+
// Head turn left/right
|
|
380
|
+
roll: { aus: [55, 56], axis: "rz", negative: 55, positive: 56 }
|
|
381
|
+
// Head tilt left/right
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
node: "EYE_L",
|
|
385
|
+
pitch: { aus: [64, 63], axis: "rx", negative: 64, positive: 63 },
|
|
386
|
+
// Eyes down/up
|
|
387
|
+
yaw: { aus: [61, 62], axis: "rz", negative: 61, positive: 62 },
|
|
388
|
+
// Eyes left/right (rz for CC4)
|
|
389
|
+
roll: null
|
|
390
|
+
// Eyes don't have roll
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
node: "EYE_R",
|
|
394
|
+
pitch: { aus: [64, 63], axis: "rx", negative: 64, positive: 63 },
|
|
395
|
+
// Eyes down/up
|
|
396
|
+
yaw: { aus: [61, 62], axis: "rz", negative: 61, positive: 62 },
|
|
397
|
+
// Eyes left/right (rz for CC4)
|
|
398
|
+
roll: null
|
|
399
|
+
// Eyes don't have roll
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
node: "TONGUE",
|
|
403
|
+
pitch: { aus: [38, 37], axis: "rz", negative: 38, positive: 37 },
|
|
404
|
+
// Tongue down/up
|
|
405
|
+
yaw: { aus: [39, 40], axis: "ry", negative: 39, positive: 40 },
|
|
406
|
+
// Tongue left/right
|
|
407
|
+
roll: { aus: [41, 42], axis: "rx", negative: 41, positive: 42 }
|
|
408
|
+
// Tongue tilt left/right
|
|
409
|
+
}
|
|
410
|
+
];
|
|
411
|
+
var CONTINUUM_PAIRS_MAP = {
|
|
412
|
+
// Eyes horizontal (yaw) - both eyes share same AUs
|
|
413
|
+
61: { pairId: 62, isNegative: true, axis: "yaw", node: "EYE_L" },
|
|
414
|
+
62: { pairId: 61, isNegative: false, axis: "yaw", node: "EYE_L" },
|
|
415
|
+
// Eyes vertical (pitch)
|
|
416
|
+
64: { pairId: 63, isNegative: true, axis: "pitch", node: "EYE_L" },
|
|
417
|
+
63: { pairId: 64, isNegative: false, axis: "pitch", node: "EYE_L" },
|
|
418
|
+
// Head yaw (turn left/right)
|
|
419
|
+
51: { pairId: 52, isNegative: true, axis: "yaw", node: "HEAD" },
|
|
420
|
+
52: { pairId: 51, isNegative: false, axis: "yaw", node: "HEAD" },
|
|
421
|
+
// Head pitch (up/down)
|
|
422
|
+
54: { pairId: 53, isNegative: true, axis: "pitch", node: "HEAD" },
|
|
423
|
+
53: { pairId: 54, isNegative: false, axis: "pitch", node: "HEAD" },
|
|
424
|
+
// Head roll (tilt left/right)
|
|
425
|
+
55: { pairId: 56, isNegative: true, axis: "roll", node: "HEAD" },
|
|
426
|
+
56: { pairId: 55, isNegative: false, axis: "roll", node: "HEAD" },
|
|
427
|
+
// Jaw yaw (left/right)
|
|
428
|
+
30: { pairId: 35, isNegative: true, axis: "yaw", node: "JAW" },
|
|
429
|
+
35: { pairId: 30, isNegative: false, axis: "yaw", node: "JAW" },
|
|
430
|
+
// Tongue yaw (left/right)
|
|
431
|
+
39: { pairId: 40, isNegative: true, axis: "yaw", node: "TONGUE" },
|
|
432
|
+
40: { pairId: 39, isNegative: false, axis: "yaw", node: "TONGUE" },
|
|
433
|
+
// Tongue pitch (up/down)
|
|
434
|
+
38: { pairId: 37, isNegative: true, axis: "pitch", node: "TONGUE" },
|
|
435
|
+
37: { pairId: 38, isNegative: false, axis: "pitch", node: "TONGUE" },
|
|
436
|
+
// Tongue roll (tilt left/right)
|
|
437
|
+
41: { pairId: 42, isNegative: true, axis: "roll", node: "TONGUE" },
|
|
438
|
+
42: { pairId: 41, isNegative: false, axis: "roll", node: "TONGUE" },
|
|
439
|
+
// Extended tongue morphs (continuum pairs)
|
|
440
|
+
73: { pairId: 74, isNegative: true, axis: "yaw", node: "TONGUE" },
|
|
441
|
+
// Tongue Narrow/Wide
|
|
442
|
+
74: { pairId: 73, isNegative: false, axis: "yaw", node: "TONGUE" },
|
|
443
|
+
76: { pairId: 77, isNegative: false, axis: "pitch", node: "TONGUE" },
|
|
444
|
+
// Tongue Tip Up/Down
|
|
445
|
+
77: { pairId: 76, isNegative: true, axis: "pitch", node: "TONGUE" }
|
|
446
|
+
};
|
|
447
|
+
var CONTINUUM_LABELS = {
|
|
448
|
+
"61-62": "Eyes \u2014 Horizontal",
|
|
449
|
+
"64-63": "Eyes \u2014 Vertical",
|
|
450
|
+
"51-52": "Head \u2014 Horizontal",
|
|
451
|
+
"54-53": "Head \u2014 Vertical",
|
|
452
|
+
"55-56": "Head \u2014 Tilt",
|
|
453
|
+
"30-35": "Jaw \u2014 Horizontal",
|
|
454
|
+
"38-37": "Tongue \u2014 Vertical",
|
|
455
|
+
"39-40": "Tongue \u2014 Horizontal",
|
|
456
|
+
"41-42": "Tongue \u2014 Tilt",
|
|
457
|
+
"73-74": "Tongue \u2014 Width",
|
|
458
|
+
"76-77": "Tongue Tip \u2014 Vertical"
|
|
459
|
+
};
|
|
357
460
|
var CC4_BONE_NODES = {
|
|
358
461
|
EYE_L: "CC_Base_L_Eye",
|
|
359
462
|
EYE_R: "CC_Base_R_Eye",
|
|
@@ -486,6 +589,41 @@ var AU_MIX_DEFAULTS = {
|
|
|
486
589
|
35: 0.5
|
|
487
590
|
// jaw left/right
|
|
488
591
|
};
|
|
592
|
+
var CC4_MESHES = {
|
|
593
|
+
// Body (6 meshes, 80 morphs each) - default render order 0
|
|
594
|
+
"CC_Base_Body_1": { category: "body", morphCount: 80 },
|
|
595
|
+
"CC_Base_Body_2": { category: "body", morphCount: 80 },
|
|
596
|
+
"CC_Base_Body_3": { category: "body", morphCount: 80 },
|
|
597
|
+
"CC_Base_Body_4": { category: "body", morphCount: 80 },
|
|
598
|
+
"CC_Base_Body_5": { category: "body", morphCount: 80 },
|
|
599
|
+
"CC_Base_Body_6": { category: "body", morphCount: 80 },
|
|
600
|
+
// Eyes (bone-driven, no morphs) - render first (behind everything)
|
|
601
|
+
"CC_Base_Eye": { category: "eye", morphCount: 0, material: { renderOrder: -10 } },
|
|
602
|
+
"CC_Base_Eye_1": { category: "eye", morphCount: 0, material: { renderOrder: -10 } },
|
|
603
|
+
"CC_Base_Eye_2": { category: "eye", morphCount: 0, material: { renderOrder: -10 } },
|
|
604
|
+
"CC_Base_Eye_3": { category: "eye", morphCount: 0, material: { renderOrder: -10 } },
|
|
605
|
+
"CC_Base_Eye_4": { category: "eye", morphCount: 0, material: { renderOrder: -10 } },
|
|
606
|
+
// Eye occlusion (94 morphs each) - render on top of eyes with transparency support
|
|
607
|
+
"CC_Base_EyeOcclusion_1": { category: "eyeOcclusion", morphCount: 94, material: { renderOrder: 2, transparent: true, opacity: 1, depthWrite: true, depthTest: true, blending: "Normal" } },
|
|
608
|
+
"CC_Base_EyeOcclusion_2": { category: "eyeOcclusion", morphCount: 94, material: { renderOrder: 2, transparent: true, opacity: 1, depthWrite: true, depthTest: true, blending: "Normal" } },
|
|
609
|
+
// Tear lines (90 morphs each) - on top of eyes/face
|
|
610
|
+
"CC_Base_TearLine_1": { category: "tearLine", morphCount: 90, material: { renderOrder: 2 } },
|
|
611
|
+
"CC_Base_TearLine_2": { category: "tearLine", morphCount: 90, material: { renderOrder: 2 } },
|
|
612
|
+
// Cornea (no morphs) - render first with eyes
|
|
613
|
+
"CC_Base_Cornea": { category: "cornea", morphCount: 0, material: { renderOrder: -10 } },
|
|
614
|
+
"CC_Base_Cornea_1": { category: "cornea", morphCount: 0, material: { renderOrder: -10 } },
|
|
615
|
+
// Teeth (no morphs, follow jaw bone) - default render order
|
|
616
|
+
"CC_Base_Teeth_1": { category: "teeth", morphCount: 0 },
|
|
617
|
+
"CC_Base_Teeth_2": { category: "teeth", morphCount: 0 },
|
|
618
|
+
// Tongue (23 morphs) - default render order
|
|
619
|
+
"CC_Base_Tongue": { category: "tongue", morphCount: 23 },
|
|
620
|
+
// Eyebrows (91 morphs each) - above face
|
|
621
|
+
"Male_Bushy_1": { category: "eyebrow", morphCount: 91, material: { renderOrder: 5 } },
|
|
622
|
+
"Male_Bushy_2": { category: "eyebrow", morphCount: 91, material: { renderOrder: 5 } },
|
|
623
|
+
// Hair (14 styling morphs each) - render last (on top of everything)
|
|
624
|
+
"Side_part_wavy_1": { category: "hair", morphCount: 14, material: { renderOrder: 10 } },
|
|
625
|
+
"Side_part_wavy_2": { category: "hair", morphCount: 14, material: { renderOrder: 10 } }
|
|
626
|
+
};
|
|
489
627
|
var MORPH_TO_MESH = {
|
|
490
628
|
// Face/AU morphs affect the main face mesh and both eyebrow meshes.
|
|
491
629
|
face: ["CC_Base_Body_1", "Male_Bushy_1", "Male_Bushy_2"],
|
|
@@ -537,6 +675,10 @@ var _LoomLargeThree = class _LoomLargeThree {
|
|
|
537
675
|
__publicField(this, "mixWeights", {});
|
|
538
676
|
// Viseme state
|
|
539
677
|
__publicField(this, "visemeValues", new Array(15).fill(0));
|
|
678
|
+
// Internal RAF loop
|
|
679
|
+
__publicField(this, "clock", new Clock());
|
|
680
|
+
__publicField(this, "rafId", null);
|
|
681
|
+
__publicField(this, "running", false);
|
|
540
682
|
this.config = config.auMappings || CC4_PRESET;
|
|
541
683
|
this.mixWeights = { ...this.config.auMixDefaults };
|
|
542
684
|
this.animation = animation || new AnimationThree();
|
|
@@ -580,7 +722,30 @@ var _LoomLargeThree = class _LoomLargeThree {
|
|
|
580
722
|
this.animation.tick(dtSeconds);
|
|
581
723
|
this.flushPendingComposites();
|
|
582
724
|
}
|
|
725
|
+
/** Start the internal RAF loop */
|
|
726
|
+
start() {
|
|
727
|
+
if (this.running) return;
|
|
728
|
+
this.running = true;
|
|
729
|
+
this.clock.start();
|
|
730
|
+
const tick = () => {
|
|
731
|
+
if (!this.running) return;
|
|
732
|
+
const dt = this.clock.getDelta();
|
|
733
|
+
this.update(dt);
|
|
734
|
+
this.rafId = requestAnimationFrame(tick);
|
|
735
|
+
};
|
|
736
|
+
this.rafId = requestAnimationFrame(tick);
|
|
737
|
+
}
|
|
738
|
+
/** Stop the internal RAF loop */
|
|
739
|
+
stop() {
|
|
740
|
+
this.running = false;
|
|
741
|
+
if (this.rafId !== null) {
|
|
742
|
+
cancelAnimationFrame(this.rafId);
|
|
743
|
+
this.rafId = null;
|
|
744
|
+
}
|
|
745
|
+
this.clock.stop();
|
|
746
|
+
}
|
|
583
747
|
dispose() {
|
|
748
|
+
this.stop();
|
|
584
749
|
this.clearTransitions();
|
|
585
750
|
this.meshes = [];
|
|
586
751
|
this.model = null;
|
|
@@ -1161,6 +1326,6 @@ var HairPhysics = class {
|
|
|
1161
1326
|
}
|
|
1162
1327
|
};
|
|
1163
1328
|
|
|
1164
|
-
export { AnimationThree, CC4_PRESET, DEFAULT_HAIR_PHYSICS_CONFIG, HairPhysics, LoomLargeThree, collectMorphMeshes, LoomLargeThree as default };
|
|
1329
|
+
export { AU_INFO, AU_MIX_DEFAULTS, AU_TO_MORPHS, AnimationThree, BONE_AU_TO_BINDINGS, CC4_BONE_NODES, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, DEFAULT_HAIR_PHYSICS_CONFIG, HairPhysics, LoomLargeThree, MORPH_TO_MESH, VISEME_KEYS, collectMorphMeshes, LoomLargeThree as default, hasLeftRightMorphs, isMixedAU };
|
|
1165
1330
|
//# sourceMappingURL=index.js.map
|
|
1166
1331
|
//# sourceMappingURL=index.js.map
|