loomlarge 0.1.0

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.
@@ -0,0 +1,631 @@
1
+ /**
2
+ * LoomLarge - AU Mapping Types
3
+ *
4
+ * Type definitions for configurable Action Unit mappings.
5
+ * Allows the engine to work with different character rigs (CC4, Mixamo, etc.)
6
+ */
7
+
8
+ /**
9
+ * AUMappingConfig - Complete configuration for AU-to-morph/bone mappings
10
+ *
11
+ * This is the main configuration object that defines how Action Units
12
+ * map to morph targets and bone transformations for a specific rig type.
13
+ */
14
+ interface AUMappingConfig {
15
+ /** AU ID to morph target names (e.g., AU 12 → ['Mouth_Smile_L', 'Mouth_Smile_R']) */
16
+ auToMorphs: Record<number, string[]>;
17
+ /** AU ID to bone bindings (e.g., AU 51 → [{ node: 'HEAD', channel: 'ry', scale: 1, maxDegrees: 30 }]) */
18
+ auToBones: Record<number, BoneBinding[]>;
19
+ /** Bone key to actual node name in the model (e.g., 'HEAD' → 'CC_Base_Head') */
20
+ boneNodes: Record<string, string>;
21
+ /** Morph category to mesh names (e.g., 'face' → ['CC_Base_Body_1']) */
22
+ morphToMesh: Record<string, string[]>;
23
+ /** Viseme keys in order (typically 15 phoneme positions) */
24
+ visemeKeys: string[];
25
+ /** Optional: Default mix weights for bone/morph blending (0 = morph only, 1 = bone only) */
26
+ auMixDefaults?: Record<number, number>;
27
+ /** Optional: AU metadata (names, muscle basis, etc.) */
28
+ auInfo?: Record<string, AUInfo>;
29
+ /** Optional: Eye mesh node fallbacks (some rigs use mesh nodes instead of bone nodes) */
30
+ eyeMeshNodes?: {
31
+ LEFT: string;
32
+ RIGHT: string;
33
+ };
34
+ }
35
+ /**
36
+ * Helper type for mesh categories in morphToMesh
37
+ */
38
+ type MorphCategory = 'face' | 'viseme' | 'eye' | 'tearLine' | 'tongue' | 'hair';
39
+
40
+ /**
41
+ * LoomLarge - Core Type Definitions
42
+ *
43
+ * Type definitions for the 3D character animation engine.
44
+ * These are framework-agnostic interfaces that work with any 3D engine.
45
+ */
46
+ /**
47
+ * TransitionHandle - returned from transition methods
48
+ * Provides promise-based completion notification plus fine-grained control.
49
+ */
50
+ interface TransitionHandle {
51
+ /** Resolves when the transition completes (or is cancelled) */
52
+ promise: Promise<void>;
53
+ /** Pause this transition (holds at current value) */
54
+ pause: () => void;
55
+ /** Resume this transition after pause */
56
+ resume: () => void;
57
+ /** Cancel this transition immediately (resolves promise) */
58
+ cancel: () => void;
59
+ }
60
+ /** Standard bone keys used in AU bindings */
61
+ type BoneKey = 'EYE_L' | 'EYE_R' | 'JAW' | 'HEAD' | 'NECK' | 'TONGUE' | string;
62
+ /**
63
+ * BoneBinding - Defines how an AU maps to bone transformations
64
+ */
65
+ interface BoneBinding {
66
+ node: BoneKey;
67
+ channel: 'rx' | 'ry' | 'rz' | 'tx' | 'ty' | 'tz';
68
+ scale: -1 | 1;
69
+ maxDegrees?: number;
70
+ maxUnits?: number;
71
+ }
72
+ /**
73
+ * RotationAxis - Defines which AUs control a specific rotation axis
74
+ */
75
+ interface RotationAxis {
76
+ aus: number[];
77
+ axis: 'rx' | 'ry' | 'rz';
78
+ negative?: number;
79
+ positive?: number;
80
+ }
81
+ /**
82
+ * CompositeRotation - Defines unified rotation axes for bones
83
+ */
84
+ interface CompositeRotation {
85
+ node: string;
86
+ pitch: RotationAxis | null;
87
+ yaw: RotationAxis | null;
88
+ roll: RotationAxis | null;
89
+ }
90
+ /**
91
+ * AUInfo - Metadata about an Action Unit
92
+ */
93
+ interface AUInfo {
94
+ id: string;
95
+ name: string;
96
+ muscularBasis?: string;
97
+ links?: string[];
98
+ faceArea?: 'Upper' | 'Lower';
99
+ facePart?: string;
100
+ }
101
+ /** Per-axis rotation state */
102
+ interface RotationAxisState {
103
+ value: number;
104
+ maxRadians: number;
105
+ }
106
+ interface CompositeRotationState {
107
+ pitch: RotationAxisState;
108
+ yaw: RotationAxisState;
109
+ roll: RotationAxisState;
110
+ }
111
+ type RotationsState = Record<string, CompositeRotationState>;
112
+
113
+ /**
114
+ * LoomLarge Engine Interface
115
+ *
116
+ * Defines the contract for 3D character animation engines.
117
+ * Implementations can target different 3D frameworks (Three.js, Babylon.js, etc.)
118
+ */
119
+
120
+ /**
121
+ * Mesh interface - minimal requirements for meshes with morph targets
122
+ */
123
+ interface LoomMesh {
124
+ name: string;
125
+ visible: boolean;
126
+ isMesh: boolean;
127
+ morphTargetInfluences?: number[];
128
+ morphTargetDictionary?: Record<string, number>;
129
+ }
130
+ /**
131
+ * Vector3-like interface
132
+ */
133
+ interface LoomVector3 {
134
+ x: number;
135
+ y: number;
136
+ z: number;
137
+ clone(): LoomVector3;
138
+ copy(v: LoomVector3): void;
139
+ }
140
+ /**
141
+ * Euler rotation interface
142
+ */
143
+ interface LoomEuler {
144
+ x: number;
145
+ y: number;
146
+ z: number;
147
+ order: string;
148
+ }
149
+ /**
150
+ * Quaternion interface
151
+ */
152
+ interface LoomQuaternion {
153
+ clone(): LoomQuaternion;
154
+ copy(q: LoomQuaternion): void;
155
+ }
156
+ /**
157
+ * Object3D interface - minimal requirements for scene objects
158
+ */
159
+ interface LoomObject3D {
160
+ name?: string;
161
+ position: LoomVector3;
162
+ quaternion: LoomQuaternion;
163
+ rotation: LoomEuler & {
164
+ set(x: number, y: number, z: number, order: string): void;
165
+ };
166
+ traverse(callback: (obj: any) => void): void;
167
+ getObjectByName(name: string): LoomObject3D | undefined;
168
+ updateMatrixWorld(force: boolean): void;
169
+ }
170
+ /**
171
+ * Payload for initializing the engine with a loaded model
172
+ */
173
+ interface ReadyPayload {
174
+ meshes: LoomMesh[];
175
+ model: LoomObject3D;
176
+ }
177
+ /**
178
+ * Configuration options for the LoomLarge engine
179
+ */
180
+ interface LoomLargeConfig {
181
+ /** AU to morph target mappings */
182
+ auMappings?: AUMappingConfig;
183
+ }
184
+ /**
185
+ * Mesh info returned from getMeshList()
186
+ */
187
+ interface MeshInfo {
188
+ name: string;
189
+ visible: boolean;
190
+ morphCount: number;
191
+ }
192
+ /**
193
+ * LoomLarge Engine Interface
194
+ *
195
+ * The main interface for controlling 3D character facial animation.
196
+ * Supports Action Units (AUs), morph targets, visemes, and bone control.
197
+ */
198
+ interface LoomLarge {
199
+ /**
200
+ * Initialize the engine with a loaded model.
201
+ * Call this after loading your 3D model.
202
+ */
203
+ onReady(payload: ReadyPayload): void;
204
+ /**
205
+ * Update animation state. Call each frame with delta time in seconds.
206
+ */
207
+ update(deltaSeconds: number): void;
208
+ /**
209
+ * Dispose engine resources and cleanup.
210
+ */
211
+ dispose(): void;
212
+ /**
213
+ * Set AU value immediately (no transition)
214
+ * @param id - AU number (e.g., 12 for smile) or string ('12L' for left side)
215
+ * @param v - Value 0-1
216
+ * @param balance - Optional L/R balance: -1 = left only, 0 = both, +1 = right only
217
+ */
218
+ setAU(id: number | string, v: number, balance?: number): void;
219
+ /**
220
+ * Transition AU value smoothly over time
221
+ * @param id - AU number or string
222
+ * @param to - Target value 0-1
223
+ * @param durationMs - Transition duration in milliseconds
224
+ * @param balance - Optional L/R balance
225
+ */
226
+ transitionAU(id: number | string, to: number, durationMs?: number, balance?: number): TransitionHandle;
227
+ /**
228
+ * Get current AU value
229
+ */
230
+ getAU(id: number): number;
231
+ /**
232
+ * Set morph target value immediately
233
+ * @param key - Morph target name
234
+ * @param v - Value 0-1
235
+ * @param meshNames - Optional specific meshes to target
236
+ */
237
+ setMorph(key: string, v: number, meshNames?: string[]): void;
238
+ /**
239
+ * Transition morph target value smoothly
240
+ * @param key - Morph target name
241
+ * @param to - Target value 0-1
242
+ * @param durationMs - Transition duration in milliseconds
243
+ * @param meshNames - Optional specific meshes to target
244
+ */
245
+ transitionMorph(key: string, to: number, durationMs?: number, meshNames?: string[]): TransitionHandle;
246
+ /**
247
+ * Set viseme value immediately (for lip-sync)
248
+ * @param visemeIndex - Viseme index 0-14
249
+ * @param value - Value 0-1
250
+ * @param jawScale - Jaw movement multiplier (default 1.0)
251
+ */
252
+ setViseme(visemeIndex: number, value: number, jawScale?: number): void;
253
+ /**
254
+ * Transition viseme value smoothly
255
+ */
256
+ transitionViseme(visemeIndex: number, to: number, durationMs?: number, jawScale?: number): TransitionHandle;
257
+ /**
258
+ * Set mix weight for an AU (blend between morph and bone contribution)
259
+ */
260
+ setAUMixWeight(id: number, weight: number): void;
261
+ /**
262
+ * Get current mix weight for an AU
263
+ */
264
+ getAUMixWeight(id: number): number;
265
+ /**
266
+ * Pause all transitions
267
+ */
268
+ pause(): void;
269
+ /**
270
+ * Resume all transitions
271
+ */
272
+ resume(): void;
273
+ /**
274
+ * Check if engine is paused
275
+ */
276
+ getPaused(): boolean;
277
+ /**
278
+ * Clear all active transitions
279
+ */
280
+ clearTransitions(): void;
281
+ /**
282
+ * Get count of active transitions
283
+ */
284
+ getActiveTransitionCount(): number;
285
+ /**
286
+ * Reset all facial animation to neutral state
287
+ */
288
+ resetToNeutral(): void;
289
+ /**
290
+ * Get list of all meshes in the model
291
+ */
292
+ getMeshList(): MeshInfo[];
293
+ /**
294
+ * Set mesh visibility
295
+ */
296
+ setMeshVisible(meshName: string, visible: boolean): void;
297
+ /**
298
+ * Update AU mappings configuration
299
+ */
300
+ setAUMappings(mappings: AUMappingConfig): void;
301
+ /**
302
+ * Get current AU mappings configuration
303
+ */
304
+ getAUMappings(): AUMappingConfig;
305
+ }
306
+
307
+ /**
308
+ * Animation System Interface
309
+ *
310
+ * Defines the contract for transition/animation systems.
311
+ * Implementations can use different interpolation strategies.
312
+ */
313
+
314
+ /**
315
+ * Animation system that manages value transitions over time.
316
+ */
317
+ interface Animation {
318
+ /**
319
+ * Update all active transitions by delta time.
320
+ * @param dtSeconds - Time elapsed since last tick in seconds
321
+ */
322
+ tick(dtSeconds: number): void;
323
+ /**
324
+ * Add or replace a transition for the given key.
325
+ * If a transition with the same key exists, it should be cancelled and replaced.
326
+ *
327
+ * @param key - Unique identifier for this transition
328
+ * @param from - Starting value
329
+ * @param to - Target value
330
+ * @param durationMs - Duration in milliseconds
331
+ * @param apply - Callback to apply the interpolated value
332
+ * @param easing - Optional easing function (default: ease-in-out)
333
+ * @returns TransitionHandle for control
334
+ */
335
+ addTransition(key: string, from: number, to: number, durationMs: number, apply: (value: number) => void, easing?: (t: number) => number): TransitionHandle;
336
+ /**
337
+ * Clear all active transitions.
338
+ */
339
+ clearTransitions(): void;
340
+ /**
341
+ * Get count of active transitions.
342
+ */
343
+ getActiveTransitionCount(): number;
344
+ }
345
+
346
+ /**
347
+ * LoomLargeThree - Three.js Implementation
348
+ *
349
+ * Default implementation of the LoomLarge interface for Three.js.
350
+ * Controls 3D character facial animation using Action Units (AUs),
351
+ * morph targets, visemes, and bone transformations.
352
+ */
353
+
354
+ declare class LoomLargeThree implements LoomLarge {
355
+ private config;
356
+ private animation;
357
+ private auValues;
358
+ private rigReady;
359
+ private missingBoneWarnings;
360
+ private rotations;
361
+ private pendingCompositeNodes;
362
+ private isPaused;
363
+ private translations;
364
+ private faceMesh;
365
+ private meshes;
366
+ private model;
367
+ private meshByName;
368
+ private morphCache;
369
+ private bones;
370
+ private mixWeights;
371
+ private visemeValues;
372
+ private static readonly VISEME_JAW_AMOUNTS;
373
+ private static readonly JAW_MAX_DEGREES;
374
+ constructor(config?: LoomLargeConfig, animation?: Animation);
375
+ onReady(payload: ReadyPayload): void;
376
+ update(deltaSeconds: number): void;
377
+ dispose(): void;
378
+ setAU(id: number | string, v: number, balance?: number): void;
379
+ transitionAU(id: number | string, to: number, durationMs?: number, balance?: number): TransitionHandle;
380
+ getAU(id: number): number;
381
+ setMorph(key: string, v: number, meshNames?: string[]): void;
382
+ transitionMorph(key: string, to: number, durationMs?: number, meshNames?: string[]): TransitionHandle;
383
+ setViseme(visemeIndex: number, value: number, jawScale?: number): void;
384
+ transitionViseme(visemeIndex: number, to: number, durationMs?: number, jawScale?: number): TransitionHandle;
385
+ setAUMixWeight(id: number, weight: number): void;
386
+ getAUMixWeight(id: number): number;
387
+ pause(): void;
388
+ resume(): void;
389
+ getPaused(): boolean;
390
+ clearTransitions(): void;
391
+ getActiveTransitionCount(): number;
392
+ resetToNeutral(): void;
393
+ getMeshList(): MeshInfo[];
394
+ setMeshVisible(meshName: string, visible: boolean): void;
395
+ setAUMappings(mappings: AUMappingConfig): void;
396
+ getAUMappings(): AUMappingConfig;
397
+ private computeSideValues;
398
+ private getMeshNamesForAU;
399
+ private getMorphValue;
400
+ private isMixedAU;
401
+ private initBoneRotations;
402
+ private updateBoneRotation;
403
+ private updateBoneTranslation;
404
+ private transitionBoneRotation;
405
+ private transitionBoneTranslation;
406
+ private flushPendingComposites;
407
+ private applyCompositeRotation;
408
+ private resolveBones;
409
+ private combineHandles;
410
+ }
411
+ /**
412
+ * Helper function to collect meshes with morph targets from a scene.
413
+ */
414
+ declare function collectMorphMeshes(root: LoomObject3D): LoomMesh[];
415
+
416
+ /**
417
+ * AnimationThree - Lerp-based animation system
418
+ *
419
+ * Default implementation of the Animation interface.
420
+ * Uses simple lerp interpolation with easing functions.
421
+ */
422
+
423
+ declare class AnimationThree implements Animation {
424
+ private transitions;
425
+ /**
426
+ * Tick all active transitions by dt seconds.
427
+ * Applies eased interpolation and removes completed transitions.
428
+ * Respects individual transition pause state.
429
+ */
430
+ tick(dtSeconds: number): void;
431
+ /**
432
+ * Add or replace a transition for the given key.
433
+ * If a transition with the same key exists, it is cancelled and replaced.
434
+ * @returns TransitionHandle with { promise, pause, resume, cancel }
435
+ */
436
+ addTransition(key: string, from: number, to: number, durationMs: number, apply: (value: number) => void, easing?: (t: number) => number): TransitionHandle;
437
+ /** Clear all running transitions. */
438
+ clearTransitions(): void;
439
+ /** Get count of active transitions. */
440
+ getActiveTransitionCount(): number;
441
+ }
442
+
443
+ /**
444
+ * Hair Physics Interface
445
+ *
446
+ * Defines the contract for hair physics simulation systems.
447
+ * Implementations can use different physics models (spring-damper, verlet, etc.)
448
+ */
449
+ /**
450
+ * Configuration for hair physics simulation
451
+ */
452
+ interface HairPhysicsConfig$1 {
453
+ mass: number;
454
+ stiffness: number;
455
+ damping: number;
456
+ gravity: number;
457
+ headInfluence: number;
458
+ windEnabled: boolean;
459
+ windStrength: number;
460
+ windDirectionX: number;
461
+ windDirectionZ: number;
462
+ windTurbulence: number;
463
+ windFrequency: number;
464
+ }
465
+ /**
466
+ * Hair strand definition for multi-strand simulation
467
+ */
468
+ interface HairStrand {
469
+ id: string;
470
+ morphKeys: {
471
+ left: string;
472
+ right: string;
473
+ front?: string;
474
+ back?: string;
475
+ };
476
+ mass?: number;
477
+ stiffness?: number;
478
+ damping?: number;
479
+ }
480
+ /**
481
+ * Current physics state of hair simulation
482
+ */
483
+ interface HairState {
484
+ x: number;
485
+ z: number;
486
+ vx: number;
487
+ vz: number;
488
+ }
489
+ /**
490
+ * Head orientation state for physics input
491
+ */
492
+ interface HeadState$1 {
493
+ yaw: number;
494
+ pitch: number;
495
+ roll: number;
496
+ yawVelocity: number;
497
+ pitchVelocity: number;
498
+ }
499
+ /**
500
+ * Output morph values from physics simulation
501
+ */
502
+ interface HairMorphOutput$1 {
503
+ [morphKey: string]: number;
504
+ }
505
+ /**
506
+ * Hair Physics simulation interface
507
+ */
508
+ interface HairPhysics$1 {
509
+ /**
510
+ * Update physics simulation
511
+ * @param dt Delta time in seconds
512
+ * @param headState Current head orientation and velocity
513
+ * @returns Morph values to apply to hair meshes
514
+ */
515
+ update(dt: number, headState: HeadState$1): HairMorphOutput$1;
516
+ /**
517
+ * Get current physics state (for debugging/UI)
518
+ */
519
+ getState(): HairState;
520
+ /**
521
+ * Update configuration
522
+ */
523
+ setConfig(config: Partial<HairPhysicsConfig$1>): void;
524
+ /**
525
+ * Get current configuration
526
+ */
527
+ getConfig(): HairPhysicsConfig$1;
528
+ /**
529
+ * Reset physics state to rest position
530
+ */
531
+ reset(): void;
532
+ }
533
+
534
+ /**
535
+ * CC4 Preset - Character Creator 4 AU Mappings
536
+ *
537
+ * Complete FACS Action Unit to morph target and bone mappings for CC4 rigs.
538
+ * This includes all the composite rotations, continuum pairs, mesh classifications,
539
+ * and AU metadata that we painstakingly worked through.
540
+ */
541
+
542
+ declare const CC4_PRESET: AUMappingConfig;
543
+
544
+ /**
545
+ * HairPhysics - Spring-damper pendulum simulation for hair movement
546
+ *
547
+ * This is a pure physics simulation class with no Three.js dependencies.
548
+ * It outputs normalized morph values (0-1) that can be applied to hair meshes
549
+ * via EngineThree.setMorph() or transitionMorph().
550
+ *
551
+ * Physics model:
552
+ * - Hair is modeled as a damped pendulum affected by:
553
+ * - Gravity (constant downward force based on head orientation)
554
+ * - Head velocity (inertia causes hair to lag behind head movement)
555
+ * - Wind (oscillating force with turbulence)
556
+ * - Spring restoration (hair returns to rest position)
557
+ * - Damping (air resistance)
558
+ */
559
+ interface HairPhysicsConfig {
560
+ mass: number;
561
+ stiffness: number;
562
+ damping: number;
563
+ gravity: number;
564
+ headInfluence: number;
565
+ windEnabled: boolean;
566
+ windStrength: number;
567
+ windDirectionX: number;
568
+ windDirectionZ: number;
569
+ windTurbulence: number;
570
+ windFrequency: number;
571
+ }
572
+ interface HairPhysicsState {
573
+ x: number;
574
+ z: number;
575
+ vx: number;
576
+ vz: number;
577
+ }
578
+ interface HairMorphOutput {
579
+ L_Hair_Left: number;
580
+ L_Hair_Right: number;
581
+ L_Hair_Front: number;
582
+ R_Hair_Left: number;
583
+ R_Hair_Right: number;
584
+ R_Hair_Front: number;
585
+ }
586
+ interface HeadState {
587
+ yaw: number;
588
+ pitch: number;
589
+ roll: number;
590
+ yawVelocity: number;
591
+ pitchVelocity: number;
592
+ }
593
+ declare const DEFAULT_HAIR_PHYSICS_CONFIG: HairPhysicsConfig;
594
+ declare class HairPhysics {
595
+ private config;
596
+ private state;
597
+ private time;
598
+ private prevHeadYaw;
599
+ private prevHeadPitch;
600
+ constructor(config?: Partial<HairPhysicsConfig>);
601
+ /**
602
+ * Update physics simulation
603
+ * @param dt Delta time in seconds
604
+ * @param headState Current head orientation and velocity
605
+ * @returns Morph values to apply to hair meshes
606
+ */
607
+ update(dt: number, headState: HeadState): HairMorphOutput;
608
+ /**
609
+ * Convert physics state to morph target values
610
+ * Maps pendulum position to left/right/front morphs for both sides
611
+ */
612
+ private computeMorphOutput;
613
+ /**
614
+ * Get current physics state (for debugging/UI)
615
+ */
616
+ getState(): HairPhysicsState;
617
+ /**
618
+ * Update configuration
619
+ */
620
+ setConfig(config: Partial<HairPhysicsConfig>): void;
621
+ /**
622
+ * Get current configuration
623
+ */
624
+ getConfig(): HairPhysicsConfig;
625
+ /**
626
+ * Reset physics state to rest position
627
+ */
628
+ reset(): void;
629
+ }
630
+
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 };