@lovelace_lol/loom3 1.0.39 → 1.0.40
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 +20 -0
- package/dist/index.cjs +196 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -1
- package/dist/index.d.ts +57 -1
- package/dist/index.js +197 -7
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -315,6 +315,33 @@ interface CompositeRotationState {
|
|
|
315
315
|
roll: number;
|
|
316
316
|
}
|
|
317
317
|
type RotationsState = Record<string, CompositeRotationState>;
|
|
318
|
+
type MorphTargetAttributeData = Float32Array | number[];
|
|
319
|
+
interface MorphTargetDelta {
|
|
320
|
+
/** Mesh name from the loaded Three.js scene. */
|
|
321
|
+
meshName: string;
|
|
322
|
+
/** Morph target name to add to morphTargetDictionary. */
|
|
323
|
+
name: string;
|
|
324
|
+
/** POSITION deltas, usually XYZ values relative to the base mesh. */
|
|
325
|
+
position: MorphTargetAttributeData;
|
|
326
|
+
/** Optional NORMAL deltas. */
|
|
327
|
+
normal?: MorphTargetAttributeData;
|
|
328
|
+
/** Optional TANGENT deltas. */
|
|
329
|
+
tangent?: MorphTargetAttributeData;
|
|
330
|
+
/** Whether deltas are relative to base attributes. Defaults to true. */
|
|
331
|
+
relative?: boolean;
|
|
332
|
+
}
|
|
333
|
+
interface AddMorphTargetOptions {
|
|
334
|
+
/** Replace an existing morph target with the same name. */
|
|
335
|
+
replace?: boolean;
|
|
336
|
+
/** Initialize the new or replaced influence value to zero. Defaults to true. */
|
|
337
|
+
resetInfluence?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* Replace and dispose the BufferGeometry instead of mutating it in place.
|
|
340
|
+
* Defaults to true because Three.js does not support mutating morph attributes
|
|
341
|
+
* after a geometry has rendered.
|
|
342
|
+
*/
|
|
343
|
+
forceGeometryReplacement?: boolean;
|
|
344
|
+
}
|
|
318
345
|
/** Source category for mixer-driven animations. */
|
|
319
346
|
type AnimationSource = 'baked' | 'clip' | 'snippet';
|
|
320
347
|
/** Shared blend-mode surface for downstream animation UIs. */
|
|
@@ -958,6 +985,25 @@ interface Animation {
|
|
|
958
985
|
* @param meshNames - Optional specific meshes to target
|
|
959
986
|
*/
|
|
960
987
|
transitionMorphInfluence(index: number, to: number, durationMs?: number, meshNames?: string[]): TransitionHandle;
|
|
988
|
+
/**
|
|
989
|
+
* Add or replace a runtime morph target on a mesh.
|
|
990
|
+
* Returns the morphTargetInfluences index assigned to the target.
|
|
991
|
+
*/
|
|
992
|
+
addMorphTarget(target: MorphTargetDelta, options?: AddMorphTargetOptions): number;
|
|
993
|
+
/**
|
|
994
|
+
* Add multiple runtime morph targets and return their assigned indices keyed
|
|
995
|
+
* by "meshName:name".
|
|
996
|
+
*/
|
|
997
|
+
addMorphTargets(targets: MorphTargetDelta[], options?: AddMorphTargetOptions): Record<string, number>;
|
|
998
|
+
/**
|
|
999
|
+
* Ensure a named morph influence slot exists on the mesh.
|
|
1000
|
+
* If the target is missing, a zero-delta morph target is created.
|
|
1001
|
+
*/
|
|
1002
|
+
ensureMorphInfluence(meshName: string, morphName: string): number;
|
|
1003
|
+
/**
|
|
1004
|
+
* Rebuild runtime morph caches after external geometry or dictionary changes.
|
|
1005
|
+
*/
|
|
1006
|
+
refreshMorphTargets(meshNames?: string[]): void;
|
|
961
1007
|
/**
|
|
962
1008
|
* Set viseme value immediately (for lip-sync)
|
|
963
1009
|
* @param visemeIndex - Viseme index 0-14
|
|
@@ -1461,6 +1507,10 @@ declare class Loom3 implements LoomLarge {
|
|
|
1461
1507
|
* @param balance - Optional L/R balance for bilateral morphs
|
|
1462
1508
|
*/
|
|
1463
1509
|
transitionContinuum(negAU: number, posAU: number, continuumValue: number, durationMs?: number, balance?: number): TransitionHandle;
|
|
1510
|
+
addMorphTarget(target: MorphTargetDelta, options?: AddMorphTargetOptions): number;
|
|
1511
|
+
addMorphTargets(targets: MorphTargetDelta[], options?: AddMorphTargetOptions): Record<string, number>;
|
|
1512
|
+
ensureMorphInfluence(meshName: string, morphName: string): number;
|
|
1513
|
+
refreshMorphTargets(_meshNames?: string[]): void;
|
|
1464
1514
|
/**
|
|
1465
1515
|
* Set a morph target value.
|
|
1466
1516
|
*
|
|
@@ -1607,6 +1657,12 @@ declare class Loom3 implements LoomLarge {
|
|
|
1607
1657
|
private applyMorphTargets;
|
|
1608
1658
|
private getMorphValue;
|
|
1609
1659
|
private getMorphValueByIndex;
|
|
1660
|
+
private applyMorphTargetDelta;
|
|
1661
|
+
private requireNamedMesh;
|
|
1662
|
+
private getMeshMorphDictionary;
|
|
1663
|
+
private setMorphAttributeAtIndex;
|
|
1664
|
+
private setZeroMorphAttributeAtIndex;
|
|
1665
|
+
private addRuntimeMorphMesh;
|
|
1610
1666
|
private getMorphKeyCacheKey;
|
|
1611
1667
|
private getMorphIndexCacheKey;
|
|
1612
1668
|
private syncVisemeRuntimeState;
|
|
@@ -2462,4 +2518,4 @@ declare function detectFacingDirection(model: THREE.Object3D, eyeBoneNames?: {
|
|
|
2462
2518
|
right: string[];
|
|
2463
2519
|
}): 'forward' | 'backward' | 'unknown';
|
|
2464
2520
|
|
|
2465
|
-
export { type AUInfo, type AUSelector, AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, type AnalyzeModelOptions, type Animation, type AnimationActionHandle, type AnimationAnalysis, type AnimationBlendMode, type AnimationClipInfo, type AnimationEasing, type AnimationInfo, type AnimationPlayOptions, type AnimationSource, type AnimationState, AnimationThree, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, type BlendingMode, type BoneBinding, type BoneInfo, type BoneKey, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, CC4_SUFFIX_PATTERN, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, type CameraRelativeGazeOffset, type CameraRelativeGazeOptions, type CharacterConfig, type CharacterRegistry, type ClipEvent, type ClipEventListener, type ClipHandle, type ClipOptions, type CompositeRotation, type CompositeRotationState, type CurvePoint, type CurvesMap, DEFAULT_HAIR_PHYSICS_CONFIG, type ExpandAnimation, type ExpandedRegionState, FISH_AU_MAPPING_CONFIG, type FaceCenterResult, type FallbackConfig, type FindFaceCenterOptions, type Hair, type HairMorphAxis, type HairMorphOutput$1 as HairMorphOutput, type HairMorphTargetMapping, type HairMorphTargetValueMapping, type HairMorphTargetsConfig, type HairObjectRef, type HairObjectState, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysicsDirectionConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsProfileConfig, type HairPhysicsRuntimeConfig, type HairPhysicsRuntimeConfigUpdate, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LineConfig, type LineCurve, type LineStyle, Loom3, type Loom3Config, Loom3 as Loom3Three, type LoomLarge, type LoomLargeConfig, Loom3 as LoomLargeThree, MORPH_TO_MESH, type MappingConsistencyResult, type MappingCorrection, type MappingCorrectionOptions, type MappingCorrectionResult, type MappingIssue, type MarkerGroup, type MarkerStyle, type MarkerStyleOverrides, type MeshCategory, type MeshInfo, type MeshMaterialSettings, type MixerLoopMode, type ModelAnalysisReport, type ModelData, type ModelMeshInfo, type MorphCategory, type MorphInfo, type MorphTargetRef, type MorphTargetsBySide, type NamedDirection, type PresetType, type Profile, type ReadyPayload, type Region, type RotationAxis, type RotationsState, type Snippet, type TrackInfo, type TransitionHandle, VISEME_JAW_AMOUNTS, VISEME_KEYS, type ValidateMappingOptions, type ValidationResult, analyzeModel, applyCharacterProfileToPreset, collectMorphMeshes, computeCameraRelativeGazeOffset, detectFacingDirection, extendCharacterConfigWithPreset, extendPresetWithProfile, extractFromGLTF, extractModelData, extractProfileOverrides, findFaceCenter, fuzzyNameMatch, generateMappingCorrections, getModelForwardDirection, getPreset, getPresetWithProfile, hasLeftRightMorphs, isMixedAU, isPresetCompatible, mergeRegionsByName as mergeCharacterRegionsByName, resolveBoneName, resolveBoneNames, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, suggestBestPreset, validateMappingConfig, validateMappings };
|
|
2521
|
+
export { type AUInfo, type AUSelector, AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, type AddMorphTargetOptions, type AnalyzeModelOptions, type Animation, type AnimationActionHandle, type AnimationAnalysis, type AnimationBlendMode, type AnimationClipInfo, type AnimationEasing, type AnimationInfo, type AnimationPlayOptions, type AnimationSource, type AnimationState, AnimationThree, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, type BlendingMode, type BoneBinding, type BoneInfo, type BoneKey, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, CC4_SUFFIX_PATTERN, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, type CameraRelativeGazeOffset, type CameraRelativeGazeOptions, type CharacterConfig, type CharacterRegistry, type ClipEvent, type ClipEventListener, type ClipHandle, type ClipOptions, type CompositeRotation, type CompositeRotationState, type CurvePoint, type CurvesMap, DEFAULT_HAIR_PHYSICS_CONFIG, type ExpandAnimation, type ExpandedRegionState, FISH_AU_MAPPING_CONFIG, type FaceCenterResult, type FallbackConfig, type FindFaceCenterOptions, type Hair, type HairMorphAxis, type HairMorphOutput$1 as HairMorphOutput, type HairMorphTargetMapping, type HairMorphTargetValueMapping, type HairMorphTargetsConfig, type HairObjectRef, type HairObjectState, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysicsDirectionConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsProfileConfig, type HairPhysicsRuntimeConfig, type HairPhysicsRuntimeConfigUpdate, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LineConfig, type LineCurve, type LineStyle, Loom3, type Loom3Config, Loom3 as Loom3Three, type LoomLarge, type LoomLargeConfig, Loom3 as LoomLargeThree, MORPH_TO_MESH, type MappingConsistencyResult, type MappingCorrection, type MappingCorrectionOptions, type MappingCorrectionResult, type MappingIssue, type MarkerGroup, type MarkerStyle, type MarkerStyleOverrides, type MeshCategory, type MeshInfo, type MeshMaterialSettings, type MixerLoopMode, type ModelAnalysisReport, type ModelData, type ModelMeshInfo, type MorphCategory, type MorphInfo, type MorphTargetAttributeData, type MorphTargetDelta, type MorphTargetRef, type MorphTargetsBySide, type NamedDirection, type PresetType, type Profile, type ReadyPayload, type Region, type RotationAxis, type RotationsState, type Snippet, type TrackInfo, type TransitionHandle, VISEME_JAW_AMOUNTS, VISEME_KEYS, type ValidateMappingOptions, type ValidationResult, analyzeModel, applyCharacterProfileToPreset, collectMorphMeshes, computeCameraRelativeGazeOffset, detectFacingDirection, extendCharacterConfigWithPreset, extendPresetWithProfile, extractFromGLTF, extractModelData, extractProfileOverrides, findFaceCenter, fuzzyNameMatch, generateMappingCorrections, getModelForwardDirection, getPreset, getPresetWithProfile, hasLeftRightMorphs, isMixedAU, isPresetCompatible, mergeRegionsByName as mergeCharacterRegionsByName, resolveBoneName, resolveBoneNames, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, suggestBestPreset, validateMappingConfig, validateMappings };
|
package/dist/index.d.ts
CHANGED
|
@@ -315,6 +315,33 @@ interface CompositeRotationState {
|
|
|
315
315
|
roll: number;
|
|
316
316
|
}
|
|
317
317
|
type RotationsState = Record<string, CompositeRotationState>;
|
|
318
|
+
type MorphTargetAttributeData = Float32Array | number[];
|
|
319
|
+
interface MorphTargetDelta {
|
|
320
|
+
/** Mesh name from the loaded Three.js scene. */
|
|
321
|
+
meshName: string;
|
|
322
|
+
/** Morph target name to add to morphTargetDictionary. */
|
|
323
|
+
name: string;
|
|
324
|
+
/** POSITION deltas, usually XYZ values relative to the base mesh. */
|
|
325
|
+
position: MorphTargetAttributeData;
|
|
326
|
+
/** Optional NORMAL deltas. */
|
|
327
|
+
normal?: MorphTargetAttributeData;
|
|
328
|
+
/** Optional TANGENT deltas. */
|
|
329
|
+
tangent?: MorphTargetAttributeData;
|
|
330
|
+
/** Whether deltas are relative to base attributes. Defaults to true. */
|
|
331
|
+
relative?: boolean;
|
|
332
|
+
}
|
|
333
|
+
interface AddMorphTargetOptions {
|
|
334
|
+
/** Replace an existing morph target with the same name. */
|
|
335
|
+
replace?: boolean;
|
|
336
|
+
/** Initialize the new or replaced influence value to zero. Defaults to true. */
|
|
337
|
+
resetInfluence?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* Replace and dispose the BufferGeometry instead of mutating it in place.
|
|
340
|
+
* Defaults to true because Three.js does not support mutating morph attributes
|
|
341
|
+
* after a geometry has rendered.
|
|
342
|
+
*/
|
|
343
|
+
forceGeometryReplacement?: boolean;
|
|
344
|
+
}
|
|
318
345
|
/** Source category for mixer-driven animations. */
|
|
319
346
|
type AnimationSource = 'baked' | 'clip' | 'snippet';
|
|
320
347
|
/** Shared blend-mode surface for downstream animation UIs. */
|
|
@@ -958,6 +985,25 @@ interface Animation {
|
|
|
958
985
|
* @param meshNames - Optional specific meshes to target
|
|
959
986
|
*/
|
|
960
987
|
transitionMorphInfluence(index: number, to: number, durationMs?: number, meshNames?: string[]): TransitionHandle;
|
|
988
|
+
/**
|
|
989
|
+
* Add or replace a runtime morph target on a mesh.
|
|
990
|
+
* Returns the morphTargetInfluences index assigned to the target.
|
|
991
|
+
*/
|
|
992
|
+
addMorphTarget(target: MorphTargetDelta, options?: AddMorphTargetOptions): number;
|
|
993
|
+
/**
|
|
994
|
+
* Add multiple runtime morph targets and return their assigned indices keyed
|
|
995
|
+
* by "meshName:name".
|
|
996
|
+
*/
|
|
997
|
+
addMorphTargets(targets: MorphTargetDelta[], options?: AddMorphTargetOptions): Record<string, number>;
|
|
998
|
+
/**
|
|
999
|
+
* Ensure a named morph influence slot exists on the mesh.
|
|
1000
|
+
* If the target is missing, a zero-delta morph target is created.
|
|
1001
|
+
*/
|
|
1002
|
+
ensureMorphInfluence(meshName: string, morphName: string): number;
|
|
1003
|
+
/**
|
|
1004
|
+
* Rebuild runtime morph caches after external geometry or dictionary changes.
|
|
1005
|
+
*/
|
|
1006
|
+
refreshMorphTargets(meshNames?: string[]): void;
|
|
961
1007
|
/**
|
|
962
1008
|
* Set viseme value immediately (for lip-sync)
|
|
963
1009
|
* @param visemeIndex - Viseme index 0-14
|
|
@@ -1461,6 +1507,10 @@ declare class Loom3 implements LoomLarge {
|
|
|
1461
1507
|
* @param balance - Optional L/R balance for bilateral morphs
|
|
1462
1508
|
*/
|
|
1463
1509
|
transitionContinuum(negAU: number, posAU: number, continuumValue: number, durationMs?: number, balance?: number): TransitionHandle;
|
|
1510
|
+
addMorphTarget(target: MorphTargetDelta, options?: AddMorphTargetOptions): number;
|
|
1511
|
+
addMorphTargets(targets: MorphTargetDelta[], options?: AddMorphTargetOptions): Record<string, number>;
|
|
1512
|
+
ensureMorphInfluence(meshName: string, morphName: string): number;
|
|
1513
|
+
refreshMorphTargets(_meshNames?: string[]): void;
|
|
1464
1514
|
/**
|
|
1465
1515
|
* Set a morph target value.
|
|
1466
1516
|
*
|
|
@@ -1607,6 +1657,12 @@ declare class Loom3 implements LoomLarge {
|
|
|
1607
1657
|
private applyMorphTargets;
|
|
1608
1658
|
private getMorphValue;
|
|
1609
1659
|
private getMorphValueByIndex;
|
|
1660
|
+
private applyMorphTargetDelta;
|
|
1661
|
+
private requireNamedMesh;
|
|
1662
|
+
private getMeshMorphDictionary;
|
|
1663
|
+
private setMorphAttributeAtIndex;
|
|
1664
|
+
private setZeroMorphAttributeAtIndex;
|
|
1665
|
+
private addRuntimeMorphMesh;
|
|
1610
1666
|
private getMorphKeyCacheKey;
|
|
1611
1667
|
private getMorphIndexCacheKey;
|
|
1612
1668
|
private syncVisemeRuntimeState;
|
|
@@ -2462,4 +2518,4 @@ declare function detectFacingDirection(model: THREE.Object3D, eyeBoneNames?: {
|
|
|
2462
2518
|
right: string[];
|
|
2463
2519
|
}): 'forward' | 'backward' | 'unknown';
|
|
2464
2520
|
|
|
2465
|
-
export { type AUInfo, type AUSelector, AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, type AnalyzeModelOptions, type Animation, type AnimationActionHandle, type AnimationAnalysis, type AnimationBlendMode, type AnimationClipInfo, type AnimationEasing, type AnimationInfo, type AnimationPlayOptions, type AnimationSource, type AnimationState, AnimationThree, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, type BlendingMode, type BoneBinding, type BoneInfo, type BoneKey, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, CC4_SUFFIX_PATTERN, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, type CameraRelativeGazeOffset, type CameraRelativeGazeOptions, type CharacterConfig, type CharacterRegistry, type ClipEvent, type ClipEventListener, type ClipHandle, type ClipOptions, type CompositeRotation, type CompositeRotationState, type CurvePoint, type CurvesMap, DEFAULT_HAIR_PHYSICS_CONFIG, type ExpandAnimation, type ExpandedRegionState, FISH_AU_MAPPING_CONFIG, type FaceCenterResult, type FallbackConfig, type FindFaceCenterOptions, type Hair, type HairMorphAxis, type HairMorphOutput$1 as HairMorphOutput, type HairMorphTargetMapping, type HairMorphTargetValueMapping, type HairMorphTargetsConfig, type HairObjectRef, type HairObjectState, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysicsDirectionConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsProfileConfig, type HairPhysicsRuntimeConfig, type HairPhysicsRuntimeConfigUpdate, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LineConfig, type LineCurve, type LineStyle, Loom3, type Loom3Config, Loom3 as Loom3Three, type LoomLarge, type LoomLargeConfig, Loom3 as LoomLargeThree, MORPH_TO_MESH, type MappingConsistencyResult, type MappingCorrection, type MappingCorrectionOptions, type MappingCorrectionResult, type MappingIssue, type MarkerGroup, type MarkerStyle, type MarkerStyleOverrides, type MeshCategory, type MeshInfo, type MeshMaterialSettings, type MixerLoopMode, type ModelAnalysisReport, type ModelData, type ModelMeshInfo, type MorphCategory, type MorphInfo, type MorphTargetRef, type MorphTargetsBySide, type NamedDirection, type PresetType, type Profile, type ReadyPayload, type Region, type RotationAxis, type RotationsState, type Snippet, type TrackInfo, type TransitionHandle, VISEME_JAW_AMOUNTS, VISEME_KEYS, type ValidateMappingOptions, type ValidationResult, analyzeModel, applyCharacterProfileToPreset, collectMorphMeshes, computeCameraRelativeGazeOffset, detectFacingDirection, extendCharacterConfigWithPreset, extendPresetWithProfile, extractFromGLTF, extractModelData, extractProfileOverrides, findFaceCenter, fuzzyNameMatch, generateMappingCorrections, getModelForwardDirection, getPreset, getPresetWithProfile, hasLeftRightMorphs, isMixedAU, isPresetCompatible, mergeRegionsByName as mergeCharacterRegionsByName, resolveBoneName, resolveBoneNames, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, suggestBestPreset, validateMappingConfig, validateMappings };
|
|
2521
|
+
export { type AUInfo, type AUSelector, AU_INFO, AU_MAPPING_CONFIG, AU_MIX_DEFAULTS, AU_TO_MORPHS, type AddMorphTargetOptions, type AnalyzeModelOptions, type Animation, type AnimationActionHandle, type AnimationAnalysis, type AnimationBlendMode, type AnimationClipInfo, type AnimationEasing, type AnimationInfo, type AnimationPlayOptions, type AnimationSource, type AnimationState, AnimationThree, BETTA_FISH_PRESET, BLENDING_MODES, BONE_AU_TO_BINDINGS, type BlendingMode, type BoneBinding, type BoneInfo, type BoneKey, CC4_BONE_NODES, CC4_BONE_PREFIX, CC4_EYE_MESH_NODES, CC4_MESHES, CC4_PRESET, CC4_SUFFIX_PATTERN, COMPOSITE_ROTATIONS, CONTINUUM_LABELS, CONTINUUM_PAIRS_MAP, type CameraRelativeGazeOffset, type CameraRelativeGazeOptions, type CharacterConfig, type CharacterRegistry, type ClipEvent, type ClipEventListener, type ClipHandle, type ClipOptions, type CompositeRotation, type CompositeRotationState, type CurvePoint, type CurvesMap, DEFAULT_HAIR_PHYSICS_CONFIG, type ExpandAnimation, type ExpandedRegionState, FISH_AU_MAPPING_CONFIG, type FaceCenterResult, type FallbackConfig, type FindFaceCenterOptions, type Hair, type HairMorphAxis, type HairMorphOutput$1 as HairMorphOutput, type HairMorphTargetMapping, type HairMorphTargetValueMapping, type HairMorphTargetsConfig, type HairObjectRef, type HairObjectState, HairPhysics, type HairPhysicsConfig$1 as HairPhysicsConfig, type HairPhysicsDirectionConfig, type HairPhysics$1 as HairPhysicsInterface, type HairMorphOutput as HairPhysicsMorphOutput, type HairPhysicsProfileConfig, type HairPhysicsRuntimeConfig, type HairPhysicsRuntimeConfigUpdate, type HairPhysicsState, type HairState, type HairStrand, type HeadState$1 as HeadState, type LineConfig, type LineCurve, type LineStyle, Loom3, type Loom3Config, Loom3 as Loom3Three, type LoomLarge, type LoomLargeConfig, Loom3 as LoomLargeThree, MORPH_TO_MESH, type MappingConsistencyResult, type MappingCorrection, type MappingCorrectionOptions, type MappingCorrectionResult, type MappingIssue, type MarkerGroup, type MarkerStyle, type MarkerStyleOverrides, type MeshCategory, type MeshInfo, type MeshMaterialSettings, type MixerLoopMode, type ModelAnalysisReport, type ModelData, type ModelMeshInfo, type MorphCategory, type MorphInfo, type MorphTargetAttributeData, type MorphTargetDelta, type MorphTargetRef, type MorphTargetsBySide, type NamedDirection, type PresetType, type Profile, type ReadyPayload, type Region, type RotationAxis, type RotationsState, type Snippet, type TrackInfo, type TransitionHandle, VISEME_JAW_AMOUNTS, VISEME_KEYS, type ValidateMappingOptions, type ValidationResult, analyzeModel, applyCharacterProfileToPreset, collectMorphMeshes, computeCameraRelativeGazeOffset, detectFacingDirection, extendCharacterConfigWithPreset, extendPresetWithProfile, extractFromGLTF, extractModelData, extractProfileOverrides, findFaceCenter, fuzzyNameMatch, generateMappingCorrections, getModelForwardDirection, getPreset, getPresetWithProfile, hasLeftRightMorphs, isMixedAU, isPresetCompatible, mergeRegionsByName as mergeCharacterRegionsByName, resolveBoneName, resolveBoneNames, resolveFaceCenter, resolvePreset, resolvePresetWithOverrides, suggestBestPreset, validateMappingConfig, validateMappings };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as THREE2 from 'three';
|
|
2
|
-
import { Vector3, Clock, Box3, Quaternion, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopPingPong, LoopOnce, LoopRepeat, QuaternionKeyframeTrack, NumberKeyframeTrack, AnimationClip, AnimationMixer, Mesh, PropertyBinding } from 'three';
|
|
2
|
+
import { Vector3, Clock, Box3, BufferAttribute, Quaternion, AdditiveAnimationBlendMode, NormalAnimationBlendMode, LoopPingPong, LoopOnce, LoopRepeat, QuaternionKeyframeTrack, NumberKeyframeTrack, AnimationClip, AnimationMixer, Mesh, PropertyBinding } from 'three';
|
|
3
3
|
|
|
4
4
|
var __defProp = Object.defineProperty;
|
|
5
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
@@ -4617,11 +4617,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4617
4617
|
this.morphIndexCache.clear();
|
|
4618
4618
|
model.traverse((obj) => {
|
|
4619
4619
|
if (obj.isMesh && obj.name) {
|
|
4620
|
-
|
|
4621
|
-
const dict = obj.morphTargetDictionary;
|
|
4622
|
-
if (Array.isArray(infl) && infl.length > 0 || dict && Object.keys(dict).length > 0) {
|
|
4623
|
-
this.meshByName.set(obj.name, obj);
|
|
4624
|
-
}
|
|
4620
|
+
this.meshByName.set(obj.name, obj);
|
|
4625
4621
|
}
|
|
4626
4622
|
});
|
|
4627
4623
|
this.bones = this.resolveBones(model);
|
|
@@ -4989,6 +4985,58 @@ var _Loom3 = class _Loom3 {
|
|
|
4989
4985
|
const currentContinuum = currentPos - currentNeg;
|
|
4990
4986
|
return this.animation.addTransition(driverKey, currentContinuum, target, durationMs, (value) => this.setContinuum(negAU, posAU, value, balance));
|
|
4991
4987
|
}
|
|
4988
|
+
// ============================================================================
|
|
4989
|
+
// MORPH CONTROL
|
|
4990
|
+
// ============================================================================
|
|
4991
|
+
addMorphTarget(target, options = {}) {
|
|
4992
|
+
const staleMorphTargets = this.collectResolvedExpressionMorphTargets();
|
|
4993
|
+
const index = this.applyMorphTargetDelta(target, options);
|
|
4994
|
+
this.refreshMorphTargets([target.meshName]);
|
|
4995
|
+
this.reinitializeRuntimeStateFromCurrentControls(staleMorphTargets);
|
|
4996
|
+
return index;
|
|
4997
|
+
}
|
|
4998
|
+
addMorphTargets(targets, options = {}) {
|
|
4999
|
+
const staleMorphTargets = this.collectResolvedExpressionMorphTargets();
|
|
5000
|
+
const result = {};
|
|
5001
|
+
for (const target of targets) {
|
|
5002
|
+
const index = this.applyMorphTargetDelta(target, options);
|
|
5003
|
+
result[`${target.meshName}:${target.name}`] = index;
|
|
5004
|
+
}
|
|
5005
|
+
this.refreshMorphTargets(Array.from(new Set(targets.map((target) => target.meshName))));
|
|
5006
|
+
this.reinitializeRuntimeStateFromCurrentControls(staleMorphTargets);
|
|
5007
|
+
return result;
|
|
5008
|
+
}
|
|
5009
|
+
ensureMorphInfluence(meshName, morphName) {
|
|
5010
|
+
const mesh = this.requireNamedMesh(meshName);
|
|
5011
|
+
const dict = this.getMeshMorphDictionary(mesh);
|
|
5012
|
+
const existing = dict[morphName];
|
|
5013
|
+
if (existing !== void 0) return existing;
|
|
5014
|
+
const position = mesh.geometry.getAttribute("position");
|
|
5015
|
+
if (!position) {
|
|
5016
|
+
throw new Error(`Cannot create morph target "${morphName}" on mesh "${meshName}": geometry has no position attribute.`);
|
|
5017
|
+
}
|
|
5018
|
+
return this.addMorphTarget({
|
|
5019
|
+
meshName,
|
|
5020
|
+
name: morphName,
|
|
5021
|
+
position: new Float32Array(position.count * position.itemSize),
|
|
5022
|
+
relative: true
|
|
5023
|
+
});
|
|
5024
|
+
}
|
|
5025
|
+
refreshMorphTargets(_meshNames) {
|
|
5026
|
+
this.morphKeyCache.clear();
|
|
5027
|
+
this.morphIndexCache.clear();
|
|
5028
|
+
if (this.model) {
|
|
5029
|
+
this.meshByName.clear();
|
|
5030
|
+
this.model.traverse((obj) => {
|
|
5031
|
+
if (obj.isMesh && obj.name) {
|
|
5032
|
+
this.meshByName.set(obj.name, obj);
|
|
5033
|
+
}
|
|
5034
|
+
});
|
|
5035
|
+
this.meshes = collectMorphMeshes(this.model);
|
|
5036
|
+
}
|
|
5037
|
+
this.rebuildMorphTargetsCache();
|
|
5038
|
+
this.hairPhysics.refreshMeshSelection();
|
|
5039
|
+
}
|
|
4992
5040
|
setMorph(key, v, meshNamesOrTargets) {
|
|
4993
5041
|
const val = clamp012(v);
|
|
4994
5042
|
if (Array.isArray(meshNamesOrTargets) && meshNamesOrTargets.length > 0 && typeof meshNamesOrTargets[0] === "object" && "infl" in meshNamesOrTargets[0]) {
|
|
@@ -5607,6 +5655,148 @@ var _Loom3 = class _Loom3 {
|
|
|
5607
5655
|
}
|
|
5608
5656
|
return 0;
|
|
5609
5657
|
}
|
|
5658
|
+
applyMorphTargetDelta(target, options) {
|
|
5659
|
+
const mesh = this.requireNamedMesh(target.meshName);
|
|
5660
|
+
const sourceGeometry = mesh.geometry;
|
|
5661
|
+
const position = sourceGeometry.getAttribute("position");
|
|
5662
|
+
if (!position) {
|
|
5663
|
+
throw new Error(`Cannot add morph target "${target.name}" to mesh "${target.meshName}": geometry has no position attribute.`);
|
|
5664
|
+
}
|
|
5665
|
+
if (!target.name || !target.name.trim()) {
|
|
5666
|
+
throw new Error(`Cannot add morph target to mesh "${target.meshName}": target name is required.`);
|
|
5667
|
+
}
|
|
5668
|
+
const replace = options.replace === true;
|
|
5669
|
+
const resetInfluence = options.resetInfluence !== false;
|
|
5670
|
+
const forceGeometryReplacement = options.forceGeometryReplacement !== false;
|
|
5671
|
+
const previousInfluences = mesh.morphTargetInfluences ? [...mesh.morphTargetInfluences] : [];
|
|
5672
|
+
const previousDictionary = this.getMeshMorphDictionary(mesh);
|
|
5673
|
+
const existingIndex = previousDictionary[target.name];
|
|
5674
|
+
if (existingIndex !== void 0 && !replace) {
|
|
5675
|
+
throw new Error(`Morph target "${target.name}" already exists on mesh "${target.meshName}". Pass replace: true to overwrite it.`);
|
|
5676
|
+
}
|
|
5677
|
+
const geometry = forceGeometryReplacement ? sourceGeometry.clone() : sourceGeometry;
|
|
5678
|
+
const dictionary = { ...previousDictionary };
|
|
5679
|
+
const usedIndices = Object.values(dictionary).filter(Number.isInteger);
|
|
5680
|
+
const existingAttributeTargetCount = Math.max(
|
|
5681
|
+
0,
|
|
5682
|
+
...Object.values(geometry.morphAttributes).map((attributes) => attributes?.length ?? 0)
|
|
5683
|
+
);
|
|
5684
|
+
const nextIndex = Math.max(existingAttributeTargetCount, usedIndices.length ? Math.max(...usedIndices) + 1 : 0);
|
|
5685
|
+
const index = existingIndex ?? nextIndex;
|
|
5686
|
+
dictionary[target.name] = index;
|
|
5687
|
+
this.setMorphAttributeAtIndex(geometry, "position", target.position, position.itemSize, position.count, index, target.name);
|
|
5688
|
+
const normal = geometry.getAttribute("normal");
|
|
5689
|
+
if (target.normal) {
|
|
5690
|
+
this.setMorphAttributeAtIndex(geometry, "normal", target.normal, normal?.itemSize ?? 3, position.count, index, target.name);
|
|
5691
|
+
} else {
|
|
5692
|
+
this.setZeroMorphAttributeAtIndex(geometry, "normal", normal?.itemSize ?? 3, position.count, index, target.name);
|
|
5693
|
+
}
|
|
5694
|
+
const tangent = geometry.getAttribute("tangent");
|
|
5695
|
+
if (target.tangent) {
|
|
5696
|
+
this.setMorphAttributeAtIndex(geometry, "tangent", target.tangent, tangent?.itemSize ?? 4, position.count, index, target.name);
|
|
5697
|
+
} else {
|
|
5698
|
+
this.setZeroMorphAttributeAtIndex(geometry, "tangent", tangent?.itemSize ?? 4, position.count, index, target.name);
|
|
5699
|
+
}
|
|
5700
|
+
const color = geometry.getAttribute("color");
|
|
5701
|
+
const existingColorMorph = geometry.morphAttributes.color?.find(Boolean);
|
|
5702
|
+
this.setZeroMorphAttributeAtIndex(
|
|
5703
|
+
geometry,
|
|
5704
|
+
"color",
|
|
5705
|
+
color?.itemSize ?? existingColorMorph?.itemSize ?? 3,
|
|
5706
|
+
position.count,
|
|
5707
|
+
index,
|
|
5708
|
+
target.name
|
|
5709
|
+
);
|
|
5710
|
+
geometry.morphTargetsRelative = target.relative !== false;
|
|
5711
|
+
geometry.morphTargetDictionary = dictionary;
|
|
5712
|
+
if (forceGeometryReplacement) {
|
|
5713
|
+
mesh.geometry = geometry;
|
|
5714
|
+
sourceGeometry.dispose();
|
|
5715
|
+
}
|
|
5716
|
+
const influenceLength = Math.max(previousInfluences.length, index + 1);
|
|
5717
|
+
const influences = previousInfluences.slice(0, influenceLength);
|
|
5718
|
+
while (influences.length < influenceLength) {
|
|
5719
|
+
influences.push(0);
|
|
5720
|
+
}
|
|
5721
|
+
if (resetInfluence) {
|
|
5722
|
+
influences[index] = 0;
|
|
5723
|
+
}
|
|
5724
|
+
mesh.morphTargetDictionary = dictionary;
|
|
5725
|
+
mesh.morphTargetInfluences = influences;
|
|
5726
|
+
this.addRuntimeMorphMesh(mesh);
|
|
5727
|
+
if (!this.config.morphToMesh?.face?.length) {
|
|
5728
|
+
this.config.morphToMesh = {
|
|
5729
|
+
...this.config.morphToMesh,
|
|
5730
|
+
face: [mesh.name]
|
|
5731
|
+
};
|
|
5732
|
+
}
|
|
5733
|
+
return index;
|
|
5734
|
+
}
|
|
5735
|
+
requireNamedMesh(meshName) {
|
|
5736
|
+
const mesh = this.meshByName.get(meshName);
|
|
5737
|
+
if (mesh) return mesh;
|
|
5738
|
+
if (this.model) {
|
|
5739
|
+
let found = null;
|
|
5740
|
+
this.model.traverse((obj) => {
|
|
5741
|
+
if (!found && obj.isMesh && obj.name === meshName) {
|
|
5742
|
+
found = obj;
|
|
5743
|
+
}
|
|
5744
|
+
});
|
|
5745
|
+
if (found) {
|
|
5746
|
+
this.meshByName.set(meshName, found);
|
|
5747
|
+
return found;
|
|
5748
|
+
}
|
|
5749
|
+
}
|
|
5750
|
+
throw new Error(`Mesh "${meshName}" was not found in the current model.`);
|
|
5751
|
+
}
|
|
5752
|
+
getMeshMorphDictionary(mesh) {
|
|
5753
|
+
const meshDictionary = mesh.morphTargetDictionary;
|
|
5754
|
+
const geometryDictionary = mesh.geometry.morphTargetDictionary;
|
|
5755
|
+
const dictionary = meshDictionary || geometryDictionary || {};
|
|
5756
|
+
mesh.morphTargetDictionary = dictionary;
|
|
5757
|
+
mesh.geometry.morphTargetDictionary = dictionary;
|
|
5758
|
+
return dictionary;
|
|
5759
|
+
}
|
|
5760
|
+
setMorphAttributeAtIndex(geometry, semantic, data, itemSize, vertexCount, index, name) {
|
|
5761
|
+
const expectedLength = vertexCount * itemSize;
|
|
5762
|
+
if (data.length !== expectedLength) {
|
|
5763
|
+
throw new Error(
|
|
5764
|
+
`Morph target "${name}" ${semantic} data has ${data.length} values; expected ${expectedLength} (${vertexCount} vertices * itemSize ${itemSize}).`
|
|
5765
|
+
);
|
|
5766
|
+
}
|
|
5767
|
+
const attributes = geometry.morphAttributes[semantic] ? [...geometry.morphAttributes[semantic]] : [];
|
|
5768
|
+
while (attributes.length < index) {
|
|
5769
|
+
const empty = new BufferAttribute(new Float32Array(expectedLength), itemSize);
|
|
5770
|
+
empty.name = `morph_${attributes.length}`;
|
|
5771
|
+
attributes.push(empty);
|
|
5772
|
+
}
|
|
5773
|
+
const values = data instanceof Float32Array ? new Float32Array(data) : Float32Array.from(data);
|
|
5774
|
+
const attribute = new BufferAttribute(values, itemSize);
|
|
5775
|
+
attribute.name = name;
|
|
5776
|
+
attributes[index] = attribute;
|
|
5777
|
+
geometry.morphAttributes[semantic] = attributes;
|
|
5778
|
+
}
|
|
5779
|
+
setZeroMorphAttributeAtIndex(geometry, semantic, itemSize, vertexCount, index, name) {
|
|
5780
|
+
if (!geometry.morphAttributes[semantic]?.length) return;
|
|
5781
|
+
const expectedLength = vertexCount * itemSize;
|
|
5782
|
+
const attributes = [...geometry.morphAttributes[semantic]];
|
|
5783
|
+
while (attributes.length < index) {
|
|
5784
|
+
const empty2 = new BufferAttribute(new Float32Array(expectedLength), itemSize);
|
|
5785
|
+
empty2.name = `morph_${attributes.length}`;
|
|
5786
|
+
attributes.push(empty2);
|
|
5787
|
+
}
|
|
5788
|
+
const empty = new BufferAttribute(new Float32Array(expectedLength), itemSize);
|
|
5789
|
+
empty.name = name;
|
|
5790
|
+
attributes[index] = empty;
|
|
5791
|
+
geometry.morphAttributes[semantic] = attributes;
|
|
5792
|
+
}
|
|
5793
|
+
addRuntimeMorphMesh(mesh) {
|
|
5794
|
+
const key = mesh.name || mesh.uuid;
|
|
5795
|
+
const exists = this.meshes.some((candidate) => (candidate.name || candidate.uuid) === key);
|
|
5796
|
+
if (!exists) {
|
|
5797
|
+
this.meshes.push(mesh);
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5610
5800
|
getMorphKeyCacheKey(key, meshNames) {
|
|
5611
5801
|
return meshNames?.length ? `key:${key}@${meshNames.join(",")}` : `key:${key}`;
|
|
5612
5802
|
}
|
|
@@ -7444,7 +7634,7 @@ function extractMorphs(meshes) {
|
|
|
7444
7634
|
for (const mesh of meshes) {
|
|
7445
7635
|
const geo = mesh.geometry;
|
|
7446
7636
|
if (!geo.morphAttributes) continue;
|
|
7447
|
-
const dict = geo.morphTargetDictionary;
|
|
7637
|
+
const dict = mesh.morphTargetDictionary || geo.morphTargetDictionary;
|
|
7448
7638
|
if (dict) {
|
|
7449
7639
|
for (const [name, index] of Object.entries(dict)) {
|
|
7450
7640
|
morphs.push({
|