@lovelace_lol/loom3 1.0.45 → 1.0.47
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 +283 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +271 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3014,7 +3014,7 @@ var CC4_VISEME_SLOTS = [
|
|
|
3014
3014
|
order: 10,
|
|
3015
3015
|
providerIds: { azure: [13], sapi: [13] },
|
|
3016
3016
|
phonemes: ["R"],
|
|
3017
|
-
matchers: ["(^|[_ .-])(v|viseme)?[_ .-]?(
|
|
3017
|
+
matchers: ["(^|[_ .-])(v|viseme)[_ .-]?r([_ .-]|$)", "(^|[_ .-])r[_ .-]?(sound|viseme)([_ .-]|$)"],
|
|
3018
3018
|
features: { jawOpen: 0.35, lipRound: 0.5 },
|
|
3019
3019
|
defaultJawAmount: VISEME_JAW_AMOUNTS[10]
|
|
3020
3020
|
},
|
|
@@ -3034,7 +3034,7 @@ var CC4_VISEME_SLOTS = [
|
|
|
3034
3034
|
order: 12,
|
|
3035
3035
|
providerIds: { azure: [14, 19], sapi: [14, 19] },
|
|
3036
3036
|
phonemes: ["T", "L", "D", "N"],
|
|
3037
|
-
matchers: ["(^|[_ .-])(v|viseme)?[_ .-]?(t[_ .-]?l[_ .-]?d[_ .-]?n|tldn|l
|
|
3037
|
+
matchers: ["(^|[_ .-])(v|viseme)?[_ .-]?(t[_ .-]?l[_ .-]?d[_ .-]?n|tldn)([_ .-]|$)", "(^|[_ .-])(v|viseme)[_ .-]?l([_ .-]|$)"],
|
|
3038
3038
|
features: { jawOpen: 0.3, tongueTip: 1 },
|
|
3039
3039
|
defaultJawAmount: VISEME_JAW_AMOUNTS[12]
|
|
3040
3040
|
},
|
|
@@ -7538,6 +7538,274 @@ function resolveFaceCenter(model, region, profile) {
|
|
|
7538
7538
|
};
|
|
7539
7539
|
}
|
|
7540
7540
|
|
|
7541
|
+
// src/camera/annotationCameraAngles.ts
|
|
7542
|
+
var DEFAULT_LATERALITY = {
|
|
7543
|
+
leftSideX: -1,
|
|
7544
|
+
confidence: 0,
|
|
7545
|
+
evidence: ["default:left=-X"]
|
|
7546
|
+
};
|
|
7547
|
+
var SIDE_TOKEN_PATTERN = /(^|[_\s-])(left|right)(?=$|[_\s-])/i;
|
|
7548
|
+
var MIN_LATERAL_SEPARATION = 1e-3;
|
|
7549
|
+
function invertHorizontalSign(sign) {
|
|
7550
|
+
return sign === 1 ? -1 : 1;
|
|
7551
|
+
}
|
|
7552
|
+
function averagePoints(points) {
|
|
7553
|
+
if (points.length === 0) return null;
|
|
7554
|
+
const center = new THREE2__namespace.Vector3();
|
|
7555
|
+
for (const point of points) {
|
|
7556
|
+
center.add(point);
|
|
7557
|
+
}
|
|
7558
|
+
return center.divideScalar(points.length);
|
|
7559
|
+
}
|
|
7560
|
+
function replaceSemanticSideToken(name, replacement) {
|
|
7561
|
+
const match = SIDE_TOKEN_PATTERN.exec(name);
|
|
7562
|
+
if (!match) return null;
|
|
7563
|
+
const [fullMatch, prefix] = match;
|
|
7564
|
+
const start = match.index;
|
|
7565
|
+
const end = start + fullMatch.length;
|
|
7566
|
+
return `${name.slice(0, start)}${prefix}${replacement}${name.slice(end)}`;
|
|
7567
|
+
}
|
|
7568
|
+
function collectMatchedPoints(model, targetNames, matcher, extractor) {
|
|
7569
|
+
const points = [];
|
|
7570
|
+
for (const targetName of targetNames) {
|
|
7571
|
+
model.traverse((obj) => {
|
|
7572
|
+
if (!matcher(obj, targetName)) return;
|
|
7573
|
+
const point = extractor(obj);
|
|
7574
|
+
if (point) {
|
|
7575
|
+
points.push(point);
|
|
7576
|
+
}
|
|
7577
|
+
});
|
|
7578
|
+
}
|
|
7579
|
+
return points;
|
|
7580
|
+
}
|
|
7581
|
+
function getRegionWorldCenter(model, region, characterConfig) {
|
|
7582
|
+
if (region.customPosition) {
|
|
7583
|
+
return new THREE2__namespace.Vector3(
|
|
7584
|
+
region.customPosition.x,
|
|
7585
|
+
region.customPosition.y,
|
|
7586
|
+
region.customPosition.z
|
|
7587
|
+
);
|
|
7588
|
+
}
|
|
7589
|
+
if (region.objects?.includes("*")) {
|
|
7590
|
+
return null;
|
|
7591
|
+
}
|
|
7592
|
+
const suffixPattern = characterConfig?.suffixPattern;
|
|
7593
|
+
const points = [];
|
|
7594
|
+
if (region.bones?.length) {
|
|
7595
|
+
const boneNames = resolveBoneNames(region.bones, characterConfig ?? void 0);
|
|
7596
|
+
points.push(
|
|
7597
|
+
...collectMatchedPoints(
|
|
7598
|
+
model,
|
|
7599
|
+
boneNames,
|
|
7600
|
+
(obj, targetName) => fuzzyNameMatch(obj.name, targetName, suffixPattern),
|
|
7601
|
+
(obj) => {
|
|
7602
|
+
const worldPos = new THREE2__namespace.Vector3();
|
|
7603
|
+
obj.getWorldPosition(worldPos);
|
|
7604
|
+
return worldPos;
|
|
7605
|
+
}
|
|
7606
|
+
)
|
|
7607
|
+
);
|
|
7608
|
+
}
|
|
7609
|
+
if (region.meshes?.length) {
|
|
7610
|
+
points.push(
|
|
7611
|
+
...collectMatchedPoints(
|
|
7612
|
+
model,
|
|
7613
|
+
region.meshes,
|
|
7614
|
+
(obj, targetName) => obj.isMesh && fuzzyNameMatch(obj.name, targetName, suffixPattern),
|
|
7615
|
+
(obj) => {
|
|
7616
|
+
const box = new THREE2__namespace.Box3().setFromObject(obj);
|
|
7617
|
+
if (box.isEmpty()) {
|
|
7618
|
+
return null;
|
|
7619
|
+
}
|
|
7620
|
+
return box.getCenter(new THREE2__namespace.Vector3());
|
|
7621
|
+
}
|
|
7622
|
+
)
|
|
7623
|
+
);
|
|
7624
|
+
}
|
|
7625
|
+
if (region.objects?.length) {
|
|
7626
|
+
const objectTargets = region.objects.filter((target) => target !== "*");
|
|
7627
|
+
points.push(
|
|
7628
|
+
...collectMatchedPoints(
|
|
7629
|
+
model,
|
|
7630
|
+
objectTargets,
|
|
7631
|
+
(obj, targetName) => fuzzyNameMatch(obj.name, targetName, suffixPattern),
|
|
7632
|
+
(obj) => {
|
|
7633
|
+
if (obj.isMesh) {
|
|
7634
|
+
const box = new THREE2__namespace.Box3().setFromObject(obj);
|
|
7635
|
+
if (!box.isEmpty()) {
|
|
7636
|
+
return box.getCenter(new THREE2__namespace.Vector3());
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
const worldPos = new THREE2__namespace.Vector3();
|
|
7640
|
+
obj.getWorldPosition(worldPos);
|
|
7641
|
+
return worldPos;
|
|
7642
|
+
}
|
|
7643
|
+
)
|
|
7644
|
+
);
|
|
7645
|
+
}
|
|
7646
|
+
return averagePoints(points);
|
|
7647
|
+
}
|
|
7648
|
+
function getRegionLocalCenter(model, region, characterConfig) {
|
|
7649
|
+
const worldCenter = getRegionWorldCenter(model, region, characterConfig);
|
|
7650
|
+
return worldCenter ? model.worldToLocal(worldCenter.clone()) : null;
|
|
7651
|
+
}
|
|
7652
|
+
function cloneLaterality(value) {
|
|
7653
|
+
return {
|
|
7654
|
+
leftSideX: value.leftSideX,
|
|
7655
|
+
confidence: value.confidence,
|
|
7656
|
+
evidence: [...value.evidence]
|
|
7657
|
+
};
|
|
7658
|
+
}
|
|
7659
|
+
function getDefaultAnnotationLaterality() {
|
|
7660
|
+
return cloneLaterality(DEFAULT_LATERALITY);
|
|
7661
|
+
}
|
|
7662
|
+
function normalizeCameraAngle(angle) {
|
|
7663
|
+
return (angle % 360 + 360) % 360;
|
|
7664
|
+
}
|
|
7665
|
+
function getRegionSemanticSide(regionName) {
|
|
7666
|
+
if (!regionName) return null;
|
|
7667
|
+
const match = SIDE_TOKEN_PATTERN.exec(regionName);
|
|
7668
|
+
if (!match) return null;
|
|
7669
|
+
return match[2].toLowerCase() === "left" ? "left" : "right";
|
|
7670
|
+
}
|
|
7671
|
+
function getSemanticHorizontalSign(regionName, laterality) {
|
|
7672
|
+
const side = getRegionSemanticSide(regionName);
|
|
7673
|
+
if (side === "left") return laterality.leftSideX;
|
|
7674
|
+
if (side === "right") return invertHorizontalSign(laterality.leftSideX);
|
|
7675
|
+
return null;
|
|
7676
|
+
}
|
|
7677
|
+
function getSemanticHorizontalSignForSide(side, laterality) {
|
|
7678
|
+
return side === "left" ? laterality.leftSideX : invertHorizontalSign(laterality.leftSideX);
|
|
7679
|
+
}
|
|
7680
|
+
function detectAnnotationLaterality(model, regions, characterConfig) {
|
|
7681
|
+
if (!model || regions.length === 0) {
|
|
7682
|
+
return getDefaultAnnotationLaterality();
|
|
7683
|
+
}
|
|
7684
|
+
model.updateMatrixWorld(true);
|
|
7685
|
+
const regionsByName = new Map(
|
|
7686
|
+
regions.map((region) => [region.name.toLowerCase(), region])
|
|
7687
|
+
);
|
|
7688
|
+
const votes = [];
|
|
7689
|
+
const pairedKeys = /* @__PURE__ */ new Set();
|
|
7690
|
+
for (const region of regions) {
|
|
7691
|
+
if (getRegionSemanticSide(region.name) !== "left") continue;
|
|
7692
|
+
const mirroredName = replaceSemanticSideToken(region.name, "right");
|
|
7693
|
+
if (!mirroredName) continue;
|
|
7694
|
+
const other = regionsByName.get(mirroredName.toLowerCase());
|
|
7695
|
+
if (!other) continue;
|
|
7696
|
+
const pairKey = [region.name.toLowerCase(), other.name.toLowerCase()].sort().join("|");
|
|
7697
|
+
if (pairedKeys.has(pairKey)) continue;
|
|
7698
|
+
pairedKeys.add(pairKey);
|
|
7699
|
+
const leftCenter = getRegionLocalCenter(model, region, characterConfig);
|
|
7700
|
+
const rightCenter = getRegionLocalCenter(model, other, characterConfig);
|
|
7701
|
+
if (!leftCenter || !rightCenter) continue;
|
|
7702
|
+
const separation = Math.abs(leftCenter.x - rightCenter.x);
|
|
7703
|
+
if (separation < MIN_LATERAL_SEPARATION) continue;
|
|
7704
|
+
votes.push({
|
|
7705
|
+
sign: leftCenter.x > rightCenter.x ? 1 : -1,
|
|
7706
|
+
weight: separation,
|
|
7707
|
+
evidence: `pair:${region.name}/${other.name}:${leftCenter.x.toFixed(3)}/${rightCenter.x.toFixed(3)}`
|
|
7708
|
+
});
|
|
7709
|
+
}
|
|
7710
|
+
if (votes.length === 0) {
|
|
7711
|
+
for (const region of regions) {
|
|
7712
|
+
const side = getRegionSemanticSide(region.name);
|
|
7713
|
+
if (!side) continue;
|
|
7714
|
+
const center = getRegionLocalCenter(model, region, characterConfig);
|
|
7715
|
+
if (!center || Math.abs(center.x) < MIN_LATERAL_SEPARATION) continue;
|
|
7716
|
+
const sign = side === "left" ? center.x > 0 ? 1 : -1 : center.x > 0 ? -1 : 1;
|
|
7717
|
+
votes.push({
|
|
7718
|
+
sign,
|
|
7719
|
+
weight: Math.abs(center.x),
|
|
7720
|
+
evidence: `single:${region.name}:${center.x.toFixed(3)}`
|
|
7721
|
+
});
|
|
7722
|
+
}
|
|
7723
|
+
}
|
|
7724
|
+
if (votes.length === 0) {
|
|
7725
|
+
return getDefaultAnnotationLaterality();
|
|
7726
|
+
}
|
|
7727
|
+
let signedWeight = 0;
|
|
7728
|
+
let totalWeight = 0;
|
|
7729
|
+
for (const vote of votes) {
|
|
7730
|
+
signedWeight += vote.sign * vote.weight;
|
|
7731
|
+
totalWeight += vote.weight;
|
|
7732
|
+
}
|
|
7733
|
+
if (Math.abs(signedWeight) < MIN_LATERAL_SEPARATION) {
|
|
7734
|
+
return getDefaultAnnotationLaterality();
|
|
7735
|
+
}
|
|
7736
|
+
return {
|
|
7737
|
+
leftSideX: signedWeight > 0 ? 1 : -1,
|
|
7738
|
+
confidence: totalWeight > 0 ? Math.abs(signedWeight) / totalWeight : 0,
|
|
7739
|
+
evidence: votes.map((vote) => vote.evidence)
|
|
7740
|
+
};
|
|
7741
|
+
}
|
|
7742
|
+
function resolveRegionCameraAngle(region, laterality) {
|
|
7743
|
+
if (region.cameraAngle === void 0) {
|
|
7744
|
+
return void 0;
|
|
7745
|
+
}
|
|
7746
|
+
const normalizedAngle = normalizeCameraAngle(region.cameraAngle);
|
|
7747
|
+
const side = getRegionSemanticSide(region.name);
|
|
7748
|
+
if (side && (normalizedAngle === 90 || normalizedAngle === 270)) {
|
|
7749
|
+
return side === "left" ? laterality.leftSideX > 0 ? 90 : 270 : laterality.leftSideX > 0 ? 270 : 90;
|
|
7750
|
+
}
|
|
7751
|
+
return normalizedAngle;
|
|
7752
|
+
}
|
|
7753
|
+
function resolveRegionVisibilityCameraAngle(region, laterality) {
|
|
7754
|
+
const explicitAngle = resolveRegionCameraAngle(region, laterality);
|
|
7755
|
+
if (explicitAngle !== void 0) {
|
|
7756
|
+
return explicitAngle;
|
|
7757
|
+
}
|
|
7758
|
+
if (!region.parent) {
|
|
7759
|
+
return void 0;
|
|
7760
|
+
}
|
|
7761
|
+
const side = getRegionSemanticSide(region.name);
|
|
7762
|
+
if (!side) {
|
|
7763
|
+
return void 0;
|
|
7764
|
+
}
|
|
7765
|
+
return side === "left" ? laterality.leftSideX > 0 ? 90 : 270 : laterality.leftSideX > 0 ? 270 : 90;
|
|
7766
|
+
}
|
|
7767
|
+
function toWorldDirection(model, localDirection) {
|
|
7768
|
+
const worldDirection = localDirection.clone();
|
|
7769
|
+
if (model) {
|
|
7770
|
+
model.updateMatrixWorld(true);
|
|
7771
|
+
const worldQuaternion = new THREE2__namespace.Quaternion();
|
|
7772
|
+
model.getWorldQuaternion(worldQuaternion);
|
|
7773
|
+
worldDirection.applyQuaternion(worldQuaternion);
|
|
7774
|
+
}
|
|
7775
|
+
return worldDirection.normalize();
|
|
7776
|
+
}
|
|
7777
|
+
function toModelLocalDirection2(model, worldDirection) {
|
|
7778
|
+
const localDirection = worldDirection.clone();
|
|
7779
|
+
if (model) {
|
|
7780
|
+
model.updateMatrixWorld(true);
|
|
7781
|
+
const worldQuaternion = new THREE2__namespace.Quaternion();
|
|
7782
|
+
model.getWorldQuaternion(worldQuaternion);
|
|
7783
|
+
localDirection.applyQuaternion(worldQuaternion.invert());
|
|
7784
|
+
}
|
|
7785
|
+
return localDirection.normalize();
|
|
7786
|
+
}
|
|
7787
|
+
function getWorldDirectionForCameraAngle(model, cameraAngle) {
|
|
7788
|
+
const angleRad = normalizeCameraAngle(cameraAngle) * Math.PI / 180;
|
|
7789
|
+
return toWorldDirection(
|
|
7790
|
+
model,
|
|
7791
|
+
new THREE2__namespace.Vector3(Math.sin(angleRad), 0, Math.cos(angleRad))
|
|
7792
|
+
);
|
|
7793
|
+
}
|
|
7794
|
+
function getModelLocalOrbitAngle(model, modelCenter, worldPosition) {
|
|
7795
|
+
const worldOffset = new THREE2__namespace.Vector3().subVectors(worldPosition, modelCenter);
|
|
7796
|
+
const localOffset = toModelLocalDirection2(model, worldOffset);
|
|
7797
|
+
return normalizeCameraAngle(Math.atan2(localOffset.x, localOffset.z) * (180 / Math.PI));
|
|
7798
|
+
}
|
|
7799
|
+
function passesMarkerCameraAngleGate(params) {
|
|
7800
|
+
const { markerAngle, currentCameraAngle, rangeDegrees = 90 } = params;
|
|
7801
|
+
if (markerAngle === void 0 || markerAngle === 0 || currentCameraAngle === void 0) {
|
|
7802
|
+
return true;
|
|
7803
|
+
}
|
|
7804
|
+
let diff = Math.abs(currentCameraAngle - markerAngle);
|
|
7805
|
+
if (diff > 180) diff = 360 - diff;
|
|
7806
|
+
return diff <= rangeDegrees;
|
|
7807
|
+
}
|
|
7808
|
+
|
|
7541
7809
|
// src/physics/HairPhysics.ts
|
|
7542
7810
|
var DEFAULT_HAIR_PHYSICS_CONFIG = {
|
|
7543
7811
|
mass: 1,
|
|
@@ -8681,6 +8949,7 @@ exports.buildMappingEditorModel = buildMappingEditorModel;
|
|
|
8681
8949
|
exports.collectMorphMeshes = collectMorphMeshes;
|
|
8682
8950
|
exports.compileVisemeKeys = compileVisemeKeys;
|
|
8683
8951
|
exports.computeCameraRelativeGazeOffset = computeCameraRelativeGazeOffset;
|
|
8952
|
+
exports.detectAnnotationLaterality = detectAnnotationLaterality;
|
|
8684
8953
|
exports.detectFacingDirection = detectFacingDirection;
|
|
8685
8954
|
exports.extendCharacterConfigWithPreset = extendCharacterConfigWithPreset;
|
|
8686
8955
|
exports.extendPresetWithProfile = extendPresetWithProfile;
|
|
@@ -8692,30 +8961,42 @@ exports.extractProfileOverrides = extractProfileOverrides;
|
|
|
8692
8961
|
exports.findFaceCenter = findFaceCenter;
|
|
8693
8962
|
exports.fuzzyNameMatch = fuzzyNameMatch;
|
|
8694
8963
|
exports.generateMappingCorrections = generateMappingCorrections;
|
|
8964
|
+
exports.getDefaultAnnotationLaterality = getDefaultAnnotationLaterality;
|
|
8695
8965
|
exports.getMeshNamesForAUProfile = getMeshNamesForAUProfile;
|
|
8696
8966
|
exports.getMeshNamesForVisemeProfile = getMeshNamesForVisemeProfile;
|
|
8697
8967
|
exports.getModelForwardDirection = getModelForwardDirection;
|
|
8968
|
+
exports.getModelLocalOrbitAngle = getModelLocalOrbitAngle;
|
|
8698
8969
|
exports.getPreset = getPreset;
|
|
8699
8970
|
exports.getPresetWithProfile = getPresetWithProfile;
|
|
8700
8971
|
exports.getProfilePresetId = getProfilePresetId;
|
|
8701
8972
|
exports.getProfileVisemeSlots = getProfileVisemeSlots;
|
|
8973
|
+
exports.getRegionSemanticSide = getRegionSemanticSide;
|
|
8974
|
+
exports.getSemanticHorizontalSign = getSemanticHorizontalSign;
|
|
8975
|
+
exports.getSemanticHorizontalSignForSide = getSemanticHorizontalSignForSide;
|
|
8702
8976
|
exports.getVisemeBindingTargets = getVisemeBindingTargets;
|
|
8703
8977
|
exports.getVisemeJawAmounts = getVisemeJawAmounts;
|
|
8704
8978
|
exports.getVisemeSlotIndex = getVisemeSlotIndex;
|
|
8979
|
+
exports.getWorldDirectionForCameraAngle = getWorldDirectionForCameraAngle;
|
|
8705
8980
|
exports.hasLeftRightMorphs = hasLeftRightMorphs;
|
|
8706
8981
|
exports.isMixedAU = isMixedAU;
|
|
8707
8982
|
exports.isPresetCompatible = isPresetCompatible;
|
|
8708
8983
|
exports.mapProviderVisemeToSlot = mapProviderVisemeToSlot;
|
|
8709
8984
|
exports.mergeCharacterRegionsByName = mergeRegionsByName;
|
|
8710
8985
|
exports.mergeProfileRegionsByName = mergeProfileRegionsByName;
|
|
8986
|
+
exports.normalizeCameraAngle = normalizeCameraAngle;
|
|
8987
|
+
exports.passesMarkerCameraAngleGate = passesMarkerCameraAngleGate;
|
|
8711
8988
|
exports.resolveBoneName = resolveBoneName;
|
|
8712
8989
|
exports.resolveBoneNames = resolveBoneNames;
|
|
8713
8990
|
exports.resolveFaceCenter = resolveFaceCenter;
|
|
8714
8991
|
exports.resolvePreset = resolvePreset;
|
|
8715
8992
|
exports.resolvePresetWithOverrides = resolvePresetWithOverrides;
|
|
8716
8993
|
exports.resolveProfileFromPreset = resolveProfileFromPreset;
|
|
8994
|
+
exports.resolveRegionCameraAngle = resolveRegionCameraAngle;
|
|
8995
|
+
exports.resolveRegionVisibilityCameraAngle = resolveRegionVisibilityCameraAngle;
|
|
8717
8996
|
exports.resolveVisemeMeshCategory = resolveVisemeMeshCategory;
|
|
8718
8997
|
exports.suggestBestPreset = suggestBestPreset;
|
|
8998
|
+
exports.toModelLocalDirection = toModelLocalDirection2;
|
|
8999
|
+
exports.toWorldDirection = toWorldDirection;
|
|
8719
9000
|
exports.validateMappingConfig = validateMappingConfig;
|
|
8720
9001
|
exports.validateMappings = validateMappings;
|
|
8721
9002
|
//# sourceMappingURL=index.cjs.map
|