@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 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)?[_ .-]?(r)([_ .-]|$)"],
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