@lovelace_lol/loom3 1.0.37 → 1.0.38

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
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var THREE = require('three');
3
+ var THREE2 = require('three');
4
4
 
5
5
  function _interopNamespace(e) {
6
6
  if (e && e.__esModule) return e;
@@ -20,7 +20,7 @@ function _interopNamespace(e) {
20
20
  return Object.freeze(n);
21
21
  }
22
22
 
23
- var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
23
+ var THREE2__namespace = /*#__PURE__*/_interopNamespace(THREE2);
24
24
 
25
25
  var __defProp = Object.defineProperty;
26
26
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
@@ -90,12 +90,12 @@ function getRuntimeClipName(sourceClipName, channel) {
90
90
  function parseTrackTarget(trackName, model) {
91
91
  let parsed;
92
92
  try {
93
- parsed = THREE.PropertyBinding.parseTrackName(trackName);
93
+ parsed = THREE2.PropertyBinding.parseTrackName(trackName);
94
94
  } catch {
95
95
  return null;
96
96
  }
97
97
  const targetKey = parsed.objectName === "bones" && parsed.objectIndex ? String(parsed.objectIndex) : parsed.nodeName;
98
- const target = targetKey ? model.getObjectByProperty("uuid", targetKey) ?? THREE.PropertyBinding.findNode(model, targetKey) : null;
98
+ const target = targetKey ? model.getObjectByProperty("uuid", targetKey) ?? THREE2.PropertyBinding.findNode(model, targetKey) : null;
99
99
  return {
100
100
  propertyName: parsed.propertyName,
101
101
  target,
@@ -183,7 +183,7 @@ function partitionBakedClip(clip, model, bones) {
183
183
  }
184
184
  runtimeClips.push({
185
185
  channel,
186
- clip: new THREE.AnimationClip(getRuntimeClipName(clip.name, channel), clip.duration, tracks)
186
+ clip: new THREE2.AnimationClip(getRuntimeClipName(clip.name, channel), clip.duration, tracks)
187
187
  });
188
188
  }
189
189
  return {
@@ -288,9 +288,9 @@ var AnimationThree = class {
288
288
  }
289
289
  };
290
290
  var makeActionId = () => `act_${Math.random().toString(36).slice(2, 8)}_${Date.now().toString(36)}`;
291
- var X_AXIS = new THREE.Vector3(1, 0, 0);
292
- var Y_AXIS = new THREE.Vector3(0, 1, 0);
293
- var Z_AXIS = new THREE.Vector3(0, 0, 1);
291
+ var X_AXIS = new THREE2.Vector3(1, 0, 0);
292
+ var Y_AXIS = new THREE2.Vector3(0, 1, 0);
293
+ var Z_AXIS = new THREE2.Vector3(0, 0, 1);
294
294
  var CLIP_EVENT_METADATA_KEY = "__loom3ClipEvents";
295
295
  var CLIP_EVENT_EPSILON = 1e-4;
296
296
  var BakedAnimationController = class {
@@ -506,14 +506,14 @@ var BakedAnimationController = class {
506
506
  const signedRate = state.reverse ? -state.playbackRate : state.playbackRate;
507
507
  action.setEffectiveTimeScale(signedRate);
508
508
  action.setEffectiveWeight(state.weight);
509
- action.blendMode = state.blendMode === "additive" ? THREE.AdditiveAnimationBlendMode : THREE.NormalAnimationBlendMode;
509
+ action.blendMode = state.blendMode === "additive" ? THREE2.AdditiveAnimationBlendMode : THREE2.NormalAnimationBlendMode;
510
510
  const reps = state.repeatCount ?? Infinity;
511
511
  if (state.loopMode === "pingpong") {
512
- action.setLoop(THREE.LoopPingPong, reps);
512
+ action.setLoop(THREE2.LoopPingPong, reps);
513
513
  } else if (state.loopMode === "once") {
514
- action.setLoop(THREE.LoopOnce, 1);
514
+ action.setLoop(THREE2.LoopOnce, 1);
515
515
  } else {
516
- action.setLoop(THREE.LoopRepeat, reps);
516
+ action.setLoop(THREE2.LoopRepeat, reps);
517
517
  }
518
518
  action.clampWhenFinished = state.loopMode === "once";
519
519
  }
@@ -1158,7 +1158,7 @@ var BakedAnimationController = class {
1158
1158
  if (!state2 && !action2) {
1159
1159
  return null;
1160
1160
  }
1161
- const loopMode2 = state2?.loopMode ?? (action2?.loop === THREE.LoopPingPong ? "pingpong" : action2?.loop === THREE.LoopOnce ? "once" : "repeat");
1161
+ const loopMode2 = state2?.loopMode ?? (action2?.loop === THREE2.LoopPingPong ? "pingpong" : action2?.loop === THREE2.LoopOnce ? "once" : "repeat");
1162
1162
  const playbackRate2 = state2?.playbackRate ?? Math.abs(action2?.getEffectiveTimeScale?.() ?? 1);
1163
1163
  const reverse2 = state2?.reverse ?? (action2?.getEffectiveTimeScale?.() ?? 1) < 0;
1164
1164
  const pausedValues = this.bakedActionGroups.get(clipName) ? Array.from(this.bakedActionGroups.get(clipName).channelActions.values()).map((entry) => entry.paused) : [];
@@ -1189,7 +1189,7 @@ var BakedAnimationController = class {
1189
1189
  if (!action) return null;
1190
1190
  const clip = action.getClip();
1191
1191
  const state = this.playbackState.get(clipName);
1192
- const loopMode = state?.loopMode ?? (action.loop === THREE.LoopPingPong ? "pingpong" : action.loop === THREE.LoopOnce ? "once" : "repeat");
1192
+ const loopMode = state?.loopMode ?? (action.loop === THREE2.LoopPingPong ? "pingpong" : action.loop === THREE2.LoopOnce ? "once" : "repeat");
1193
1193
  const playbackRate = state?.playbackRate ?? Math.abs(action.getEffectiveTimeScale());
1194
1194
  const reverse = state?.reverse ?? action.getEffectiveTimeScale() < 0;
1195
1195
  return {
@@ -1376,12 +1376,12 @@ var BakedAnimationController = class {
1376
1376
  const jawBinding = config.auToBones[26]?.[0];
1377
1377
  const maxDegrees = jawBinding?.maxDegrees ?? 30;
1378
1378
  const radians = maxDegrees * Math.PI / 180 * jawAmount;
1379
- const jawQ = new THREE.Quaternion().copy(jawEntry.baseQuat);
1380
- jawQ.multiply(new THREE.Quaternion().setFromAxisAngle(Z_AXIS, radians));
1379
+ const jawQ = new THREE2.Quaternion().copy(jawEntry.baseQuat);
1380
+ jawQ.multiply(new THREE2.Quaternion().setFromAxisAngle(Z_AXIS, radians));
1381
1381
  jawValues.push(jawQ.x, jawQ.y, jawQ.z, jawQ.w);
1382
1382
  }
1383
1383
  const trackName = `${jawEntry.obj.uuid}.quaternion`;
1384
- tracks.push(new THREE.QuaternionKeyframeTrack(trackName, keyframeTimes, jawValues));
1384
+ tracks.push(new THREE2.QuaternionKeyframeTrack(trackName, keyframeTimes, jawValues));
1385
1385
  }
1386
1386
  }
1387
1387
  if (keyframeTimes.length > 0) {
@@ -1425,7 +1425,7 @@ var BakedAnimationController = class {
1425
1425
  }
1426
1426
  const values = [];
1427
1427
  for (const t of keyframeTimes) {
1428
- const compositeQ = new THREE.Quaternion().copy(entry.baseQuat);
1428
+ const compositeQ = new THREE2.Quaternion().copy(entry.baseQuat);
1429
1429
  const applyAxis = (axisConfig) => {
1430
1430
  if (!axisConfig) return;
1431
1431
  let axisValue = getAxisValue(nodeKey, axisConfig, t);
@@ -1434,7 +1434,7 @@ var BakedAnimationController = class {
1434
1434
  if (!binding?.maxDegrees || !binding.channel) return;
1435
1435
  const radians = binding.maxDegrees * Math.PI / 180 * Math.abs(axisValue) * binding.scale;
1436
1436
  const axis = binding.channel === "rx" ? X_AXIS : binding.channel === "ry" ? Y_AXIS : Z_AXIS;
1437
- const deltaQ = new THREE.Quaternion().setFromAxisAngle(axis, radians);
1437
+ const deltaQ = new THREE2.Quaternion().setFromAxisAngle(axis, radians);
1438
1438
  compositeQ.multiply(deltaQ);
1439
1439
  };
1440
1440
  applyAxis(composite.yaw);
@@ -1443,7 +1443,7 @@ var BakedAnimationController = class {
1443
1443
  values.push(compositeQ.x, compositeQ.y, compositeQ.z, compositeQ.w);
1444
1444
  }
1445
1445
  const trackName = `${entry.obj.uuid}.quaternion`;
1446
- tracks.push(new THREE.QuaternionKeyframeTrack(trackName, keyframeTimes, values));
1446
+ tracks.push(new THREE2.QuaternionKeyframeTrack(trackName, keyframeTimes, values));
1447
1447
  }
1448
1448
  for (const curveId of Object.keys(curves)) {
1449
1449
  if (!isNumericAU(curveId)) continue;
@@ -1464,7 +1464,7 @@ var BakedAnimationController = class {
1464
1464
  values.push(basePos + delta);
1465
1465
  }
1466
1466
  const trackName = `${entry.obj.uuid}.position[${axisIndex}]`;
1467
- tracks.push(new THREE.NumberKeyframeTrack(trackName, keyframeTimes, values));
1467
+ tracks.push(new THREE2.NumberKeyframeTrack(trackName, keyframeTimes, values));
1468
1468
  }
1469
1469
  }
1470
1470
  }
@@ -1472,7 +1472,7 @@ var BakedAnimationController = class {
1472
1472
  console.warn(`[Loom3] snippetToClip: No tracks created for "${clipName}"`);
1473
1473
  return null;
1474
1474
  }
1475
- const clip = new THREE.AnimationClip(clipName, maxTime, tracks);
1475
+ const clip = new THREE2.AnimationClip(clipName, maxTime, tracks);
1476
1476
  this.setClipEventMetadata(clip, { keyframeTimes });
1477
1477
  console.log(`[Loom3] snippetToClip: Created clip "${clipName}" with ${tracks.length} tracks, duration ${maxTime.toFixed(2)}s`);
1478
1478
  return clip;
@@ -1754,7 +1754,7 @@ var BakedAnimationController = class {
1754
1754
  values.push(Math.max(0, Math.min(2, kf.intensity * intensityScale)));
1755
1755
  }
1756
1756
  const trackName = `${mesh.uuid}.morphTargetInfluences[${morphIndex}]`;
1757
- const track = new THREE.NumberKeyframeTrack(trackName, times, values);
1757
+ const track = new THREE2.NumberKeyframeTrack(trackName, times, values);
1758
1758
  tracks.push(track);
1759
1759
  };
1760
1760
  for (const mesh of targetMeshes) {
@@ -1777,7 +1777,7 @@ var BakedAnimationController = class {
1777
1777
  values.push(Math.max(0, Math.min(2, kf.intensity * intensityScale)));
1778
1778
  }
1779
1779
  const trackName = `${mesh.uuid}.morphTargetInfluences[${morphIndex}]`;
1780
- const track = new THREE.NumberKeyframeTrack(trackName, times, values);
1780
+ const track = new THREE2.NumberKeyframeTrack(trackName, times, values);
1781
1781
  tracks.push(track);
1782
1782
  };
1783
1783
  for (const mesh of targetMeshes) {
@@ -1788,7 +1788,7 @@ var BakedAnimationController = class {
1788
1788
  const model = this.host.getModel();
1789
1789
  if (!model) return null;
1790
1790
  if (!this.animationMixer) {
1791
- this.animationMixer = new THREE.AnimationMixer(model);
1791
+ this.animationMixer = new THREE2.AnimationMixer(model);
1792
1792
  }
1793
1793
  if (this.animationMixer && !this.mixerFinishedListenerAttached) {
1794
1794
  this.animationMixer.addEventListener("finished", (event) => {
@@ -2925,7 +2925,7 @@ var HairPhysicsController = class {
2925
2925
  this.clearRegisteredHairObjects();
2926
2926
  const result = [];
2927
2927
  for (const obj of objects) {
2928
- if (obj instanceof THREE.Mesh) {
2928
+ if (obj instanceof THREE2.Mesh) {
2929
2929
  const mesh = obj;
2930
2930
  this.registeredHairObjects.set(mesh.name, mesh);
2931
2931
  const meshInfo = CC4_MESHES[mesh.name];
@@ -4515,9 +4515,9 @@ var resolvePresetWithOverrides = getPresetWithProfile;
4515
4515
 
4516
4516
  // src/engines/three/Loom3.ts
4517
4517
  var deg2rad = (d) => d * Math.PI / 180;
4518
- var X_AXIS2 = new THREE.Vector3(1, 0, 0);
4519
- var Y_AXIS2 = new THREE.Vector3(0, 1, 0);
4520
- var Z_AXIS2 = new THREE.Vector3(0, 0, 1);
4518
+ var X_AXIS2 = new THREE2.Vector3(1, 0, 0);
4519
+ var Y_AXIS2 = new THREE2.Vector3(0, 1, 0);
4520
+ var Z_AXIS2 = new THREE2.Vector3(0, 0, 1);
4521
4521
  function buildAUToCompositeMap(composites) {
4522
4522
  const map = /* @__PURE__ */ new Map();
4523
4523
  composites.forEach((comp) => {
@@ -4581,7 +4581,7 @@ var _Loom3 = class _Loom3 {
4581
4581
  __publicField(this, "bakedAnimations");
4582
4582
  __publicField(this, "hairPhysics");
4583
4583
  // Internal animation loop
4584
- __publicField(this, "clock", new THREE.Clock(false));
4584
+ __publicField(this, "clock", new THREE2.Clock(false));
4585
4585
  // Don't auto-start
4586
4586
  __publicField(this, "animationFrameId", null);
4587
4587
  __publicField(this, "isRunning", false);
@@ -4728,11 +4728,11 @@ var _Loom3 = class _Loom3 {
4728
4728
  }
4729
4729
  const head = this.bones["HEAD"]?.obj;
4730
4730
  if (head && availableMorphMeshes.length > 0) {
4731
- const headPos = new THREE.Vector3();
4731
+ const headPos = new THREE2.Vector3();
4732
4732
  head.getWorldPosition?.(headPos);
4733
4733
  const headCandidates = availableMorphMeshes.map((mesh) => {
4734
- const box = new THREE.Box3().setFromObject(mesh);
4735
- const center = new THREE.Vector3();
4734
+ const box = new THREE2.Box3().setFromObject(mesh);
4735
+ const center = new THREE2.Vector3();
4736
4736
  box.getCenter(center);
4737
4737
  const distance = box.containsPoint(headPos) ? 0 : center.distanceTo(headPos);
4738
4738
  const morphCount = mesh.morphTargetDictionary ? Object.keys(mesh.morphTargetDictionary).length : 0;
@@ -5767,13 +5767,13 @@ var _Loom3 = class _Loom3 {
5767
5767
  return;
5768
5768
  }
5769
5769
  const getAxis = (channel) => channel === "rx" ? X_AXIS2 : channel === "ry" ? Y_AXIS2 : Z_AXIS2;
5770
- const compositeQ = new THREE.Quaternion().copy(baseQuat);
5770
+ const compositeQ = new THREE2.Quaternion().copy(baseQuat);
5771
5771
  if (config.yaw && rotState.yaw !== 0) {
5772
5772
  const binding = this.getCompositeAxisBindingForNode(nodeKey, config.yaw, rotState.yaw);
5773
5773
  if (binding?.maxDegrees && binding.channel) {
5774
5774
  const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.yaw) * binding.scale;
5775
5775
  const axis = getAxis(binding.channel);
5776
- const deltaQ = new THREE.Quaternion().setFromAxisAngle(axis, radians);
5776
+ const deltaQ = new THREE2.Quaternion().setFromAxisAngle(axis, radians);
5777
5777
  compositeQ.multiply(deltaQ);
5778
5778
  }
5779
5779
  }
@@ -5782,7 +5782,7 @@ var _Loom3 = class _Loom3 {
5782
5782
  if (binding?.maxDegrees && binding.channel) {
5783
5783
  const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.pitch) * binding.scale;
5784
5784
  const axis = getAxis(binding.channel);
5785
- const deltaQ = new THREE.Quaternion().setFromAxisAngle(axis, radians);
5785
+ const deltaQ = new THREE2.Quaternion().setFromAxisAngle(axis, radians);
5786
5786
  compositeQ.multiply(deltaQ);
5787
5787
  }
5788
5788
  }
@@ -5791,7 +5791,7 @@ var _Loom3 = class _Loom3 {
5791
5791
  if (binding?.maxDegrees && binding.channel) {
5792
5792
  const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.roll) * binding.scale;
5793
5793
  const axis = getAxis(binding.channel);
5794
- const deltaQ = new THREE.Quaternion().setFromAxisAngle(axis, radians);
5794
+ const deltaQ = new THREE2.Quaternion().setFromAxisAngle(axis, radians);
5795
5795
  compositeQ.multiply(deltaQ);
5796
5796
  }
5797
5797
  }
@@ -6230,10 +6230,11 @@ function extractProfileOverrides(config) {
6230
6230
  if (key === "annotationRegions") {
6231
6231
  const topLevelAnnotationRegions = Array.isArray(topLevelConfig.annotationRegions) ? topLevelConfig.annotationRegions : void 0;
6232
6232
  const legacyAnnotationRegions = Array.isArray(legacyNestedOverrides.annotationRegions) ? legacyNestedOverrides.annotationRegions : void 0;
6233
- const presetOverrideRegions = mergeRegionsByName(legacyAnnotationRegions, topLevelAnnotationRegions);
6233
+ const legacyRegionFallback = Array.isArray(config.regions) && config.regions.length > 0 ? config.regions : void 0;
6234
+ const legacyProfileRegions = mergeRegionsByName(legacyRegionFallback, legacyAnnotationRegions);
6234
6235
  const regions = mergeRegionsByName(
6235
- presetOverrideRegions,
6236
- Array.isArray(config.regions) && config.regions.length > 0 ? config.regions : void 0
6236
+ legacyProfileRegions,
6237
+ topLevelAnnotationRegions
6237
6238
  );
6238
6239
  if (regions) {
6239
6240
  overrides.annotationRegions = regions.map((region) => cloneRegion(region));
@@ -6249,6 +6250,13 @@ function extractProfileOverrides(config) {
6249
6250
  }
6250
6251
  return overrides;
6251
6252
  }
6253
+ function hasCanonicalAnnotationRegionOverrides(config) {
6254
+ const topLevelConfig = config;
6255
+ if (Array.isArray(topLevelConfig.annotationRegions)) {
6256
+ return true;
6257
+ }
6258
+ return isPlainObject2(config.profile) && Array.isArray(config.profile.annotationRegions);
6259
+ }
6252
6260
  function applyCharacterProfileToPreset(config) {
6253
6261
  const presetType = config.auPresetType;
6254
6262
  if (!presetType) {
@@ -6286,7 +6294,7 @@ function extendCharacterConfigWithPreset(config) {
6286
6294
  return config;
6287
6295
  }
6288
6296
  const presetRegions = extendedPresetProfile.annotationRegions;
6289
- const mergedRegions = mergeRegionsByName(presetRegions, config.regions);
6297
+ const mergedRegions = hasCanonicalAnnotationRegionOverrides(config) ? mergeRegionsByName(config.regions, presetRegions) : mergeRegionsByName(presetRegions, config.regions);
6290
6298
  const normalizedRegions = normalizeRegionTree(
6291
6299
  mergedRegions,
6292
6300
  profileOverrides.disabledRegions
@@ -6301,6 +6309,43 @@ function extendCharacterConfigWithPreset(config) {
6301
6309
  regions: extendedRegions ?? config.regions
6302
6310
  };
6303
6311
  }
6312
+ var DEFAULT_EPSILON = 1e-4;
6313
+ var DEFAULT_YAW_WEIGHT = 0.35;
6314
+ var DEFAULT_PITCH_WEIGHT = 0.2;
6315
+ var ZERO_OFFSET = { x: 0, y: 0 };
6316
+ function clampOffset(value) {
6317
+ return THREE2__namespace.MathUtils.clamp(value, -1, 1);
6318
+ }
6319
+ function toModelLocalDirection(model, worldDirection) {
6320
+ const localDirection = worldDirection.clone();
6321
+ model.updateWorldMatrix(true, false);
6322
+ const worldQuaternion = new THREE2__namespace.Quaternion();
6323
+ model.getWorldQuaternion(worldQuaternion);
6324
+ localDirection.applyQuaternion(worldQuaternion.invert());
6325
+ return localDirection.normalize();
6326
+ }
6327
+ function computeCameraRelativeGazeOffset(model, cameraPosition, targetPosition, options = {}) {
6328
+ if (!model) {
6329
+ return ZERO_OFFSET;
6330
+ }
6331
+ const epsilon = options.epsilon ?? DEFAULT_EPSILON;
6332
+ const yawWeight = options.yawWeight ?? DEFAULT_YAW_WEIGHT;
6333
+ const pitchWeight = options.pitchWeight ?? DEFAULT_PITCH_WEIGHT;
6334
+ const worldOffset = new THREE2__namespace.Vector3().subVectors(cameraPosition, targetPosition);
6335
+ if (worldOffset.lengthSq() < epsilon) {
6336
+ return ZERO_OFFSET;
6337
+ }
6338
+ const localDirection = toModelLocalDirection(model, worldOffset.normalize());
6339
+ const yawAngle = Math.atan2(localDirection.x, localDirection.z);
6340
+ const pitchAngle = Math.atan2(
6341
+ localDirection.y,
6342
+ Math.max(Math.hypot(localDirection.x, localDirection.z), epsilon)
6343
+ );
6344
+ return {
6345
+ x: clampOffset(yawAngle / (Math.PI / 2)) * yawWeight,
6346
+ y: clampOffset(pitchAngle / (Math.PI / 3)) * pitchWeight
6347
+ };
6348
+ }
6304
6349
  var DEFAULT_HEAD_BONE_NAMES = ["CC_Base_Head", "Head", "head", "Bip01_Head"];
6305
6350
  var DEFAULT_REFERENCE_HEIGHT = 1.8;
6306
6351
  var DEFAULT_FORWARD_OFFSET = 0.08;
@@ -6316,8 +6361,8 @@ function findFaceCenter(model, options = {}) {
6316
6361
  referenceHeight = DEFAULT_REFERENCE_HEIGHT
6317
6362
  } = options;
6318
6363
  const debugInfo = [];
6319
- const boundingBox = new THREE__namespace.Box3().setFromObject(model);
6320
- const modelSize = new THREE__namespace.Vector3();
6364
+ const boundingBox = new THREE2__namespace.Box3().setFromObject(model);
6365
+ const modelSize = new THREE2__namespace.Vector3();
6321
6366
  boundingBox.getSize(modelSize);
6322
6367
  const scale = modelSize.y / referenceHeight;
6323
6368
  debugInfo.push(`Model height: ${modelSize.y.toFixed(2)}m, scale factor: ${scale.toFixed(2)}`);
@@ -6328,7 +6373,7 @@ function findFaceCenter(model, options = {}) {
6328
6373
  const objName = obj.name.toLowerCase();
6329
6374
  for (const boneName of headBoneNames) {
6330
6375
  if (objName === boneName.toLowerCase() || objName.includes(boneName.toLowerCase())) {
6331
- headPos2 = new THREE__namespace.Vector3();
6376
+ headPos2 = new THREE2__namespace.Vector3();
6332
6377
  obj.getWorldPosition(headPos2);
6333
6378
  debugInfo.push(`Found head bone "${obj.name}" at (${headPos2.x.toFixed(3)}, ${headPos2.y.toFixed(3)}, ${headPos2.z.toFixed(3)})`);
6334
6379
  return;
@@ -6347,7 +6392,7 @@ function findFaceCenter(model, options = {}) {
6347
6392
  if (!eyes2.left) {
6348
6393
  for (const name of EYE_BONE_NAMES.left) {
6349
6394
  if (objName.includes(name)) {
6350
- eyes2.left = new THREE__namespace.Vector3();
6395
+ eyes2.left = new THREE2__namespace.Vector3();
6351
6396
  obj.getWorldPosition(eyes2.left);
6352
6397
  debugInfo.push(`Found left eye "${obj.name}" at (${eyes2.left.x.toFixed(3)}, ${eyes2.left.y.toFixed(3)}, ${eyes2.left.z.toFixed(3)})`);
6353
6398
  break;
@@ -6357,7 +6402,7 @@ function findFaceCenter(model, options = {}) {
6357
6402
  if (!eyes2.right) {
6358
6403
  for (const name of EYE_BONE_NAMES.right) {
6359
6404
  if (objName.includes(name)) {
6360
- eyes2.right = new THREE__namespace.Vector3();
6405
+ eyes2.right = new THREE2__namespace.Vector3();
6361
6406
  obj.getWorldPosition(eyes2.right);
6362
6407
  debugInfo.push(`Found right eye "${obj.name}" at (${eyes2.right.x.toFixed(3)}, ${eyes2.right.y.toFixed(3)}, ${eyes2.right.z.toFixed(3)})`);
6363
6408
  break;
@@ -6369,7 +6414,7 @@ function findFaceCenter(model, options = {}) {
6369
6414
  };
6370
6415
  if (faceMeshNames && faceMeshNames.length > 0) {
6371
6416
  debugInfo.push(`Looking for face meshes: ${faceMeshNames.join(", ")}`);
6372
- const meshBox = new THREE__namespace.Box3();
6417
+ const meshBox = new THREE2__namespace.Box3();
6373
6418
  let foundMesh = false;
6374
6419
  model.traverse((obj) => {
6375
6420
  const isMesh = obj.isMesh;
@@ -6377,7 +6422,7 @@ function findFaceCenter(model, options = {}) {
6377
6422
  for (const meshName of faceMeshNames) {
6378
6423
  if (obj.name === meshName || obj.name.includes(meshName)) {
6379
6424
  const mesh = obj;
6380
- const tempBox = new THREE__namespace.Box3().setFromObject(mesh);
6425
+ const tempBox = new THREE2__namespace.Box3().setFromObject(mesh);
6381
6426
  meshBox.union(tempBox);
6382
6427
  foundMesh = true;
6383
6428
  debugInfo.push(`Found mesh: "${obj.name}"`);
@@ -6386,11 +6431,11 @@ function findFaceCenter(model, options = {}) {
6386
6431
  }
6387
6432
  });
6388
6433
  if (foundMesh && !meshBox.isEmpty()) {
6389
- const meshBoundsSize = new THREE__namespace.Vector3();
6434
+ const meshBoundsSize = new THREE2__namespace.Vector3();
6390
6435
  meshBox.getSize(meshBoundsSize);
6391
6436
  const isFullBodyMesh = meshBoundsSize.y > modelSize.y * 0.7;
6392
6437
  if (!isFullBodyMesh) {
6393
- const meshCenter = new THREE__namespace.Vector3();
6438
+ const meshCenter = new THREE2__namespace.Vector3();
6394
6439
  meshBox.getCenter(meshCenter);
6395
6440
  debugInfo.push(`Using face mesh center: (${meshCenter.x.toFixed(3)}, ${meshCenter.y.toFixed(3)}, ${meshCenter.z.toFixed(3)})`);
6396
6441
  return {
@@ -6404,7 +6449,7 @@ function findFaceCenter(model, options = {}) {
6404
6449
  }
6405
6450
  const eyes = findEyes();
6406
6451
  if (eyes.left && eyes.right) {
6407
- const eyeCenter = new THREE__namespace.Vector3().addVectors(eyes.left, eyes.right).multiplyScalar(0.5);
6452
+ const eyeCenter = new THREE2__namespace.Vector3().addVectors(eyes.left, eyes.right).multiplyScalar(0.5);
6408
6453
  debugInfo.push(`Eye center (face position): (${eyeCenter.x.toFixed(3)}, ${eyeCenter.y.toFixed(3)}, ${eyeCenter.z.toFixed(3)})`);
6409
6454
  const headPos2 = findHeadBone();
6410
6455
  return {
@@ -6429,10 +6474,10 @@ function findFaceCenter(model, options = {}) {
6429
6474
  };
6430
6475
  }
6431
6476
  debugInfo.push("No head bone found, using model center fallback");
6432
- const modelCenter = new THREE__namespace.Vector3();
6477
+ const modelCenter = new THREE2__namespace.Vector3();
6433
6478
  boundingBox.getCenter(modelCenter);
6434
6479
  const headHeight = boundingBox.min.y + modelSize.y * 0.9;
6435
- const fallbackCenter = new THREE__namespace.Vector3(modelCenter.x, headHeight, modelCenter.z);
6480
+ const fallbackCenter = new THREE2__namespace.Vector3(modelCenter.x, headHeight, modelCenter.z);
6436
6481
  debugInfo.push(`Fallback center: (${fallbackCenter.x.toFixed(3)}, ${fallbackCenter.y.toFixed(3)}, ${fallbackCenter.z.toFixed(3)})`);
6437
6482
  return {
6438
6483
  center: fallbackCenter,
@@ -6441,7 +6486,7 @@ function findFaceCenter(model, options = {}) {
6441
6486
  };
6442
6487
  }
6443
6488
  function getModelForwardDirection(model) {
6444
- const forward = new THREE__namespace.Vector3(0, 0, 1);
6489
+ const forward = new THREE2__namespace.Vector3(0, 0, 1);
6445
6490
  forward.applyQuaternion(model.quaternion);
6446
6491
  return forward.normalize();
6447
6492
  }
@@ -6458,7 +6503,7 @@ function detectFacingDirection(model, eyeBoneNames = {
6458
6503
  if (!eyes.left) {
6459
6504
  for (const name of eyeBoneNames.left) {
6460
6505
  if (objName.includes(name)) {
6461
- eyes.left = new THREE__namespace.Vector3();
6506
+ eyes.left = new THREE2__namespace.Vector3();
6462
6507
  obj.getWorldPosition(eyes.left);
6463
6508
  break;
6464
6509
  }
@@ -6467,7 +6512,7 @@ function detectFacingDirection(model, eyeBoneNames = {
6467
6512
  if (!eyes.right) {
6468
6513
  for (const name of eyeBoneNames.right) {
6469
6514
  if (objName.includes(name)) {
6470
- eyes.right = new THREE__namespace.Vector3();
6515
+ eyes.right = new THREE2__namespace.Vector3();
6471
6516
  obj.getWorldPosition(eyes.right);
6472
6517
  break;
6473
6518
  }
@@ -7383,19 +7428,19 @@ function extractBones(root) {
7383
7428
  const bones = [];
7384
7429
  const boneDepths = /* @__PURE__ */ new Map();
7385
7430
  root.traverse((obj) => {
7386
- if (obj instanceof THREE__namespace.Bone || obj.type === "Bone") {
7387
- const worldPos = new THREE__namespace.Vector3();
7431
+ if (obj instanceof THREE2__namespace.Bone || obj.type === "Bone") {
7432
+ const worldPos = new THREE2__namespace.Vector3();
7388
7433
  obj.getWorldPosition(worldPos);
7389
7434
  let depth = 0;
7390
7435
  let parent = obj.parent;
7391
7436
  while (parent) {
7392
- if (parent instanceof THREE__namespace.Bone || parent.type === "Bone") {
7437
+ if (parent instanceof THREE2__namespace.Bone || parent.type === "Bone") {
7393
7438
  depth++;
7394
7439
  }
7395
7440
  parent = parent.parent;
7396
7441
  }
7397
7442
  boneDepths.set(obj.name, depth);
7398
- const parentBone = obj.parent instanceof THREE__namespace.Bone || obj.parent?.type === "Bone" ? obj.parent.name : null;
7443
+ const parentBone = obj.parent instanceof THREE2__namespace.Bone || obj.parent?.type === "Bone" ? obj.parent.name : null;
7399
7444
  bones.push({
7400
7445
  name: obj.name,
7401
7446
  parent: parentBone,
@@ -7648,6 +7693,7 @@ exports.VISEME_KEYS = VISEME_KEYS;
7648
7693
  exports.analyzeModel = analyzeModel;
7649
7694
  exports.applyCharacterProfileToPreset = applyCharacterProfileToPreset;
7650
7695
  exports.collectMorphMeshes = collectMorphMeshes;
7696
+ exports.computeCameraRelativeGazeOffset = computeCameraRelativeGazeOffset;
7651
7697
  exports.detectFacingDirection = detectFacingDirection;
7652
7698
  exports.extendCharacterConfigWithPreset = extendCharacterConfigWithPreset;
7653
7699
  exports.extendPresetWithProfile = extendPresetWithProfile;