@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 +106 -60
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -3
- package/dist/index.d.ts +19 -3
- package/dist/index.js +70 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
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
|
|
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 =
|
|
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) ??
|
|
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
|
|
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
|
|
292
|
-
var Y_AXIS = new
|
|
293
|
-
var Z_AXIS = new
|
|
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" ?
|
|
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(
|
|
512
|
+
action.setLoop(THREE2.LoopPingPong, reps);
|
|
513
513
|
} else if (state.loopMode === "once") {
|
|
514
|
-
action.setLoop(
|
|
514
|
+
action.setLoop(THREE2.LoopOnce, 1);
|
|
515
515
|
} else {
|
|
516
|
-
action.setLoop(
|
|
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 ===
|
|
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 ===
|
|
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
|
|
1380
|
-
jawQ.multiply(new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
4519
|
-
var Y_AXIS2 = new
|
|
4520
|
-
var Z_AXIS2 = new
|
|
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
|
|
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
|
|
4731
|
+
const headPos = new THREE2.Vector3();
|
|
4732
4732
|
head.getWorldPosition?.(headPos);
|
|
4733
4733
|
const headCandidates = availableMorphMeshes.map((mesh) => {
|
|
4734
|
-
const box = new
|
|
4735
|
-
const center = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
6236
|
-
|
|
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
|
|
6320
|
-
const modelSize = new
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
7387
|
-
const worldPos = new
|
|
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
|
|
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
|
|
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;
|