@sage-rsc/talking-head-react 1.4.8 → 1.5.0
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 +2 -2
- package/dist/index.js +45 -45
- package/package.json +1 -1
- package/src/lib/talkinghead.mjs +26 -22
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsxs as Pe, jsx as ye } from "react/jsx-runtime";
|
|
2
|
-
import { forwardRef as Me, useRef as W, useState as pe, useEffect as ce, useCallback as O, useImperativeHandle as Fe, useLayoutEffect as
|
|
2
|
+
import { forwardRef as Me, useRef as W, useState as pe, useEffect as ce, useCallback as O, useImperativeHandle as Fe, useLayoutEffect as Ze } from "react";
|
|
3
3
|
import * as y from "three";
|
|
4
4
|
import { OrbitControls as je } from "three/addons/controls/OrbitControls.js";
|
|
5
5
|
import { GLTFLoader as Ye } from "three/addons/loaders/GLTFLoader.js";
|
|
@@ -2629,7 +2629,7 @@ const ct = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
|
2629
2629
|
fr: rt,
|
|
2630
2630
|
fi: ut,
|
|
2631
2631
|
lt: ct
|
|
2632
|
-
}, $ = new y.Quaternion(),
|
|
2632
|
+
}, $ = new y.Quaternion(), X = new y.Euler(), ve = new y.Vector3(), Re = new y.Vector3(), We = new y.Box3();
|
|
2633
2633
|
new y.Matrix4();
|
|
2634
2634
|
new y.Matrix4();
|
|
2635
2635
|
new y.Vector3();
|
|
@@ -4418,16 +4418,16 @@ class Be {
|
|
|
4418
4418
|
if (!this.armature) return;
|
|
4419
4419
|
const t = this.armature.getObjectByName("LeftShoulder"), e = this.armature.getObjectByName("RightShoulder");
|
|
4420
4420
|
if (!t || !e) return;
|
|
4421
|
-
const n = new y.Euler(), i = new y.Quaternion(),
|
|
4421
|
+
const n = new y.Euler(), i = new y.Quaternion(), o = this.mixer && this.currentFBXAction && this.currentFBXAction.isRunning() ? 0.5 : 0.6, l = 0.7;
|
|
4422
4422
|
if (t.quaternion) {
|
|
4423
4423
|
n.setFromQuaternion(t.quaternion, "XYZ");
|
|
4424
|
-
const
|
|
4425
|
-
n.x >
|
|
4424
|
+
const h = n.x;
|
|
4425
|
+
n.x > l ? n.x = l + (n.x - l) * (1 - o) : n.x > 0.5 && (n.x = n.x * (1 - o * 0.5)), Math.abs(n.x - h) > 0.01 && (i.setFromEuler(n, "XYZ"), t.quaternion.copy(i), t.updateMatrixWorld(!0));
|
|
4426
4426
|
}
|
|
4427
4427
|
if (e.quaternion) {
|
|
4428
4428
|
n.setFromQuaternion(e.quaternion, "XYZ");
|
|
4429
|
-
const
|
|
4430
|
-
n.x >
|
|
4429
|
+
const h = n.x;
|
|
4430
|
+
n.x > l ? n.x = l + (n.x - l) * (1 - o) : n.x > 0.5 && (n.x = n.x * (1 - o * 0.5)), Math.abs(n.x - h) > 0.01 && (i.setFromEuler(n, "XYZ"), e.quaternion.copy(i), e.updateMatrixWorld(!0));
|
|
4431
4431
|
}
|
|
4432
4432
|
}
|
|
4433
4433
|
/**
|
|
@@ -4436,9 +4436,9 @@ class Be {
|
|
|
4436
4436
|
updatePoseDelta() {
|
|
4437
4437
|
for (const [t, e] of Object.entries(this.poseDelta.props)) {
|
|
4438
4438
|
if (e.x === 0 && e.y === 0 && e.z === 0) continue;
|
|
4439
|
-
|
|
4439
|
+
X.set(e.x, e.y, e.z);
|
|
4440
4440
|
const n = this.poseAvatar.props[t];
|
|
4441
|
-
n.isQuaternion ? ($.setFromEuler(
|
|
4441
|
+
n.isQuaternion ? ($.setFromEuler(X), n.multiply($)) : n.isVector3 && n.add(X);
|
|
4442
4442
|
}
|
|
4443
4443
|
}
|
|
4444
4444
|
/**
|
|
@@ -5210,7 +5210,7 @@ class Be {
|
|
|
5210
5210
|
}, i.x ? new y.Vector3(i.x, i.y, i.z) : null, !0, i.d);
|
|
5211
5211
|
break;
|
|
5212
5212
|
}
|
|
5213
|
-
if ((h || r) && (
|
|
5213
|
+
if ((h || r) && (X.setFromQuaternion(this.poseAvatar.props["Head.quaternion"]), X.x = Math.max(-0.9, Math.min(0.9, 2 * X.x - 0.5)), X.y = Math.max(-0.9, Math.min(0.9, -2.5 * X.y)), h ? (Object.assign(this.mtAvatar.eyesLookDown, { system: X.x < 0 ? -X.x : 0, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyesLookUp, { system: X.x < 0 ? 0 : X.x, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookInLeft, { system: X.y < 0 ? -X.y : 0, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookOutLeft, { system: X.y < 0 ? 0 : X.y, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookInRight, { system: X.y < 0 ? 0 : X.y, needsUpdate: !0 }), Object.assign(this.mtAvatar.eyeLookOutRight, { system: X.y < 0 ? -X.y : 0, needsUpdate: !0 }), r && (n = -this.mtAvatar.bodyRotateY.value, i = this.gaussianRandom(-0.2, 0.2), this.animQueue.push(this.animFactory({
|
|
5214
5214
|
name: "headmove",
|
|
5215
5215
|
dt: [[1e3, 2e3], [1e3, 2e3, 1, 2], [1e3, 2e3], [1e3, 2e3, 1, 2]],
|
|
5216
5216
|
vs: {
|
|
@@ -6199,10 +6199,10 @@ class Be {
|
|
|
6199
6199
|
}
|
|
6200
6200
|
this.objectLeftEye.updateMatrixWorld(!0), this.objectRightEye.updateMatrixWorld(!0), ve.setFromMatrixPosition(this.objectLeftEye.matrixWorld), Re.setFromMatrixPosition(this.objectRightEye.matrixWorld), ve.add(Re).divideScalar(2), $.copy(this.armature.quaternion), $.multiply(this.poseTarget.props["Hips.quaternion"]), $.multiply(this.poseTarget.props["Spine.quaternion"]), $.multiply(this.poseTarget.props["Spine1.quaternion"]), $.multiply(this.poseTarget.props["Spine2.quaternion"]), $.multiply(this.poseTarget.props["Neck.quaternion"]), $.multiply(this.poseTarget.props["Head.quaternion"]);
|
|
6201
6201
|
const n = new y.Vector3().subVectors(e, ve).normalize(), i = Math.atan2(n.x, n.z), s = Math.asin(-n.y);
|
|
6202
|
-
|
|
6203
|
-
const l = new y.Quaternion().setFromEuler(
|
|
6204
|
-
|
|
6205
|
-
let r =
|
|
6202
|
+
X.set(s, i, 0, "YXZ");
|
|
6203
|
+
const l = new y.Quaternion().setFromEuler(X), h = new y.Quaternion().copy(l).multiply($.clone().invert());
|
|
6204
|
+
X.setFromQuaternion(h, "YXZ");
|
|
6205
|
+
let r = X.x / (40 / 24) + 0.2, u = X.y / (9 / 4), a = Math.min(0.6, Math.max(-0.3, r)), d = Math.min(0.8, Math.max(-0.8, u)), c = (Math.random() - 0.5) / 4, g = (Math.random() - 0.5) / 4;
|
|
6206
6206
|
if (t) {
|
|
6207
6207
|
let x = this.animQueue.findIndex((k) => k.template.name === "lookat");
|
|
6208
6208
|
x !== -1 && this.animQueue.splice(x, 1);
|
|
@@ -6237,8 +6237,8 @@ class Be {
|
|
|
6237
6237
|
const s = new y.Vector3().setFromMatrixPosition(this.objectLeftEye.matrixWorld), o = new y.Vector3().setFromMatrixPosition(this.objectRightEye.matrixWorld), l = new y.Vector3().addVectors(s, o).divideScalar(2);
|
|
6238
6238
|
l.project(this.camera);
|
|
6239
6239
|
let h = (l.x + 1) / 2 * i.width + i.left, r = -(l.y - 1) / 2 * i.height + i.top;
|
|
6240
|
-
t === null && (t = h), e === null && (e = r), $.copy(this.armature.quaternion), $.multiply(this.poseTarget.props["Hips.quaternion"]), $.multiply(this.poseTarget.props["Spine.quaternion"]), $.multiply(this.poseTarget.props["Spine1.quaternion"]), $.multiply(this.poseTarget.props["Spine2.quaternion"]), $.multiply(this.poseTarget.props["Neck.quaternion"]), $.multiply(this.poseTarget.props["Head.quaternion"]),
|
|
6241
|
-
let u =
|
|
6240
|
+
t === null && (t = h), e === null && (e = r), $.copy(this.armature.quaternion), $.multiply(this.poseTarget.props["Hips.quaternion"]), $.multiply(this.poseTarget.props["Spine.quaternion"]), $.multiply(this.poseTarget.props["Spine1.quaternion"]), $.multiply(this.poseTarget.props["Spine2.quaternion"]), $.multiply(this.poseTarget.props["Neck.quaternion"]), $.multiply(this.poseTarget.props["Head.quaternion"]), X.setFromQuaternion($);
|
|
6241
|
+
let u = X.x / (40 / 24), a = X.y / (9 / 4), d = Math.min(0.4, Math.max(-0.4, this.camera.rotation.x)), c = Math.min(0.4, Math.max(-0.4, this.camera.rotation.y)), g = Math.max(window.innerWidth - h, h), x = Math.max(window.innerHeight - r, r), f = this.convertRange(e, [r - x, r + x], [-0.3, 0.6]) - u + d, k = this.convertRange(t, [h - g, h + g], [-0.8, 0.8]) - a + c;
|
|
6242
6242
|
f = Math.min(0.6, Math.max(-0.3, f)), k = Math.min(0.8, Math.max(-0.8, k));
|
|
6243
6243
|
let D = (Math.random() - 0.5) / 4, p = (Math.random() - 0.5) / 4;
|
|
6244
6244
|
if (n) {
|
|
@@ -6581,8 +6581,8 @@ class Be {
|
|
|
6581
6581
|
if (d.tracks.forEach((L) => {
|
|
6582
6582
|
const z = L.name.replaceAll("mixamorig", "").split("."), Y = z[0], S = z[1], E = x(Y);
|
|
6583
6583
|
if (E && S) {
|
|
6584
|
-
const j = `${E}.${S}`,
|
|
6585
|
-
|
|
6584
|
+
const j = `${E}.${S}`, Z = L.clone();
|
|
6585
|
+
Z.name = j, p.push(Z), Y !== E && g.set(Y, E);
|
|
6586
6586
|
} else
|
|
6587
6587
|
B.add(Y), (Y.toLowerCase().includes("arm") || Y.toLowerCase().includes("hand") || Y.toLowerCase().includes("shoulder")) && console.warn(`⚠️ Arm bone "${Y}" could not be mapped to avatar skeleton`);
|
|
6588
6588
|
}), B.size > 0 && console.warn(`⚠️ ${B.size} bone(s) could not be mapped:`, Array.from(B).sort().join(", ")), p.length > 0) {
|
|
@@ -6853,7 +6853,7 @@ const Ve = Me(({
|
|
|
6853
6853
|
style: x = {},
|
|
6854
6854
|
animations: f = {}
|
|
6855
6855
|
}, k) => {
|
|
6856
|
-
const D = W(null), p = W(null), B = W(r), T = W(null), R = W(null), L = W(!1), I = W({ remainingText: null, originalText: null, options: null }), z = W([]), Y = W(0), [S, E] = pe(!0), [j,
|
|
6856
|
+
const D = W(null), p = W(null), B = W(r), T = W(null), R = W(null), L = W(!1), I = W({ remainingText: null, originalText: null, options: null }), z = W([]), Y = W(0), [S, E] = pe(!0), [j, Z] = pe(null), [oe, le] = pe(!1), [ge, de] = pe(!1);
|
|
6857
6857
|
ce(() => {
|
|
6858
6858
|
L.current = ge;
|
|
6859
6859
|
}, [ge]), ce(() => {
|
|
@@ -6902,7 +6902,7 @@ const Ve = Me(({
|
|
|
6902
6902
|
}, F = O(async () => {
|
|
6903
6903
|
if (!(!D.current || p.current))
|
|
6904
6904
|
try {
|
|
6905
|
-
if (E(!0),
|
|
6905
|
+
if (E(!0), Z(null), p.current = new Be(D.current, v), p.current.controls && (p.current.controls.enableRotate = !1, p.current.controls.enableZoom = !1, p.current.controls.enablePan = !1, p.current.controls.enableDamping = !1), f && Object.keys(f).length > 0 && (p.current.customAnimations = f), await p.current.showAvatar(b, (K) => {
|
|
6906
6906
|
if (K.lengthComputable) {
|
|
6907
6907
|
const ne = Math.min(100, Math.round(K.loaded / K.total * 100));
|
|
6908
6908
|
d(ne);
|
|
@@ -6926,7 +6926,7 @@ const Ve = Me(({
|
|
|
6926
6926
|
document.removeEventListener("visibilitychange", U);
|
|
6927
6927
|
};
|
|
6928
6928
|
} catch (w) {
|
|
6929
|
-
console.error("Error initializing TalkingHead:", w),
|
|
6929
|
+
console.error("Error initializing TalkingHead:", w), Z(w.message || "Failed to initialize avatar"), E(!1), c(w);
|
|
6930
6930
|
}
|
|
6931
6931
|
}, [V, t, e, n, i, s, o, r, l, h, u]);
|
|
6932
6932
|
ce(() => (F(), () => {
|
|
@@ -6987,8 +6987,8 @@ const Ve = Me(({
|
|
|
6987
6987
|
ie = !0, H && (clearInterval(H), H = null, R.current = null);
|
|
6988
6988
|
try {
|
|
6989
6989
|
U.onSpeechEnd();
|
|
6990
|
-
} catch (
|
|
6991
|
-
console.error("Error in onSpeechEnd callback:",
|
|
6990
|
+
} catch (Xe) {
|
|
6991
|
+
console.error("Error in onSpeechEnd callback:", Xe);
|
|
6992
6992
|
}
|
|
6993
6993
|
}
|
|
6994
6994
|
}, 100);
|
|
@@ -6998,7 +6998,7 @@ const Ve = Me(({
|
|
|
6998
6998
|
await P(), p.current && p.current.lipsync && (p.current.setSlowdownRate && p.current.setSlowdownRate(1.05), p.current.speakText(w, me));
|
|
6999
6999
|
}, 100);
|
|
7000
7000
|
} catch (K) {
|
|
7001
|
-
console.error("Error speaking text:", K),
|
|
7001
|
+
console.error("Error speaking text:", K), Z(K.message || "Failed to speak text");
|
|
7002
7002
|
}
|
|
7003
7003
|
}, [oe, P, b.lipsyncLang]), te = O(() => {
|
|
7004
7004
|
p.current && (p.current.stopSpeaking(), p.current.setSlowdownRate && p.current.setSlowdownRate(1), T.current = null, de(!1));
|
|
@@ -7255,20 +7255,20 @@ const pt = Me(({
|
|
|
7255
7255
|
try {
|
|
7256
7256
|
if (a(!0), c(null), r.current = new Be(h.current, B), await r.current.showAvatar(p, (j) => {
|
|
7257
7257
|
if (j.lengthComputable) {
|
|
7258
|
-
const
|
|
7259
|
-
t(
|
|
7258
|
+
const Z = Math.min(100, Math.round(j.loaded / j.total * 100));
|
|
7259
|
+
t(Z);
|
|
7260
7260
|
}
|
|
7261
7261
|
}), r.current.morphs && r.current.morphs.length > 0) {
|
|
7262
7262
|
const j = r.current.morphs[0].morphTargetDictionary;
|
|
7263
7263
|
console.log("Available morph targets:", Object.keys(j));
|
|
7264
|
-
const
|
|
7265
|
-
console.log("Viseme morph targets found:",
|
|
7264
|
+
const Z = Object.keys(j).filter((oe) => oe.startsWith("viseme_"));
|
|
7265
|
+
console.log("Viseme morph targets found:", Z), Z.length === 0 && (console.warn("No viseme morph targets found! Lip-sync will not work properly."), console.log("Expected viseme targets: viseme_aa, viseme_E, viseme_I, viseme_O, viseme_U, viseme_PP, viseme_SS, viseme_TH, viseme_DD, viseme_FF, viseme_kk, viseme_nn, viseme_RR, viseme_CH, viseme_sil"));
|
|
7266
7266
|
}
|
|
7267
7267
|
if (await new Promise((j) => {
|
|
7268
|
-
const
|
|
7269
|
-
r.current.lipsync && Object.keys(r.current.lipsync).length > 0 ? (console.log("Lip-sync modules loaded:", Object.keys(r.current.lipsync)), j()) : (console.log("Waiting for lip-sync modules to load..."), setTimeout(
|
|
7268
|
+
const Z = () => {
|
|
7269
|
+
r.current.lipsync && Object.keys(r.current.lipsync).length > 0 ? (console.log("Lip-sync modules loaded:", Object.keys(r.current.lipsync)), j()) : (console.log("Waiting for lip-sync modules to load..."), setTimeout(Z, 100));
|
|
7270
7270
|
};
|
|
7271
|
-
|
|
7271
|
+
Z();
|
|
7272
7272
|
}), r.current && r.current.setShowFullAvatar)
|
|
7273
7273
|
try {
|
|
7274
7274
|
r.current.setShowFullAvatar(!0), console.log("Avatar initialized in full body mode");
|
|
@@ -7311,14 +7311,14 @@ const pt = Me(({
|
|
|
7311
7311
|
if (r.current.setShowFullAvatar)
|
|
7312
7312
|
try {
|
|
7313
7313
|
r.current.setShowFullAvatar(!0);
|
|
7314
|
-
} catch (
|
|
7315
|
-
console.warn("Error setting full body mode:",
|
|
7314
|
+
} catch (Z) {
|
|
7315
|
+
console.warn("Error setting full body mode:", Z);
|
|
7316
7316
|
}
|
|
7317
7317
|
if (S.includes("."))
|
|
7318
7318
|
try {
|
|
7319
7319
|
r.current.playAnimation(S, null, 10, 0, 0.01, E), console.log("Playing animation:", S);
|
|
7320
|
-
} catch (
|
|
7321
|
-
console.log(`Failed to play ${S}:`,
|
|
7320
|
+
} catch (Z) {
|
|
7321
|
+
console.log(`Failed to play ${S}:`, Z);
|
|
7322
7322
|
try {
|
|
7323
7323
|
r.current.setBodyMovement("idle"), console.log("Fallback to idle animation");
|
|
7324
7324
|
} catch (oe) {
|
|
@@ -7326,9 +7326,9 @@ const pt = Me(({
|
|
|
7326
7326
|
}
|
|
7327
7327
|
}
|
|
7328
7328
|
else {
|
|
7329
|
-
const
|
|
7329
|
+
const Z = [".fbx", ".glb", ".gltf"];
|
|
7330
7330
|
let oe = !1;
|
|
7331
|
-
for (const le of
|
|
7331
|
+
for (const le of Z)
|
|
7332
7332
|
try {
|
|
7333
7333
|
r.current.playAnimation(S + le, null, 10, 0, 0.01, E), console.log("Playing animation:", S + le), oe = !0;
|
|
7334
7334
|
break;
|
|
@@ -7497,7 +7497,7 @@ const yt = Me(({
|
|
|
7497
7497
|
// e.g., "idle" - will randomly select from this group when idle
|
|
7498
7498
|
autoSpeak: T = !1
|
|
7499
7499
|
}, R) => {
|
|
7500
|
-
const L = W(null), I = W(null), z = W(u), Y = W(null), S = W(null), E = W(!1), j = W({ remainingText: null, originalText: null, options: null }),
|
|
7500
|
+
const L = W(null), I = W(null), z = W(u), Y = W(null), S = W(null), E = W(!1), j = W({ remainingText: null, originalText: null, options: null }), Z = W([]), [oe, le] = pe(!0), [ge, de] = pe(null), [ee, be] = pe(!1), [Q, b] = pe(!1), [v, F] = pe(D), P = W(null);
|
|
7501
7501
|
ce(() => {
|
|
7502
7502
|
E.current = Q;
|
|
7503
7503
|
}, [Q]), ce(() => {
|
|
@@ -7637,9 +7637,9 @@ const yt = Me(({
|
|
|
7637
7637
|
}
|
|
7638
7638
|
await Le();
|
|
7639
7639
|
const G = H.animationGroup || p;
|
|
7640
|
-
G && !H.skipAnimation ? (console.log(`🎬 Attempting to play animation from group: "${G}"`), console.log(`📊 Current avatarBody: "${e}", loadedAnimations:`, v), w(G)) : console.log(`⏭️ Skipping animation (group: ${G}, skipAnimation: ${H.skipAnimation})`), j.current = { remainingText: null, originalText: null, options: null },
|
|
7640
|
+
G && !H.skipAnimation ? (console.log(`🎬 Attempting to play animation from group: "${G}"`), console.log(`📊 Current avatarBody: "${e}", loadedAnimations:`, v), w(G)) : console.log(`⏭️ Skipping animation (group: ${G}, skipAnimation: ${H.skipAnimation})`), j.current = { remainingText: null, originalText: null, options: null }, Z.current = [], Y.current = { text: A, options: H }, S.current && (clearInterval(S.current), S.current = null), b(!1), E.current = !1;
|
|
7641
7641
|
const J = A.split(/[.!?]+/).filter((se) => se.trim().length > 0);
|
|
7642
|
-
|
|
7642
|
+
Z.current = J;
|
|
7643
7643
|
const ie = {
|
|
7644
7644
|
lipsyncLang: H.lipsyncLang || "en",
|
|
7645
7645
|
onSpeechEnd: () => {
|
|
@@ -8021,7 +8021,7 @@ const ft = Me(({
|
|
|
8021
8021
|
}), u.current && (u.current.setMood("happy"), u.current.setBodyMovement("idle"));
|
|
8022
8022
|
} else
|
|
8023
8023
|
k.current && k.current();
|
|
8024
|
-
}, []),
|
|
8024
|
+
}, []), Z = O(() => {
|
|
8025
8025
|
const b = R();
|
|
8026
8026
|
let v = null;
|
|
8027
8027
|
if (b?.avatar_script && b?.body) {
|
|
@@ -8242,11 +8242,11 @@ const ft = Me(({
|
|
|
8242
8242
|
c.current && c.current();
|
|
8243
8243
|
}, 10);
|
|
8244
8244
|
}, [h, R]);
|
|
8245
|
-
|
|
8246
|
-
c.current =
|
|
8245
|
+
Ze(() => {
|
|
8246
|
+
c.current = Z, g.current = j, x.current = z, f.current = E, k.current = Y, D.current = S, p.current = oe;
|
|
8247
8247
|
}), Fe(r, () => ({
|
|
8248
8248
|
// Curriculum control methods
|
|
8249
|
-
startTeaching:
|
|
8249
|
+
startTeaching: Z,
|
|
8250
8250
|
startQuestions: S,
|
|
8251
8251
|
handleAnswerSelect: oe,
|
|
8252
8252
|
handleCodeTestResult: le,
|
|
@@ -8310,7 +8310,7 @@ const ft = Me(({
|
|
|
8310
8310
|
handleResize: () => u.current?.handleResize(),
|
|
8311
8311
|
// Avatar readiness check (always returns current value)
|
|
8312
8312
|
isAvatarReady: () => u.current?.isReady || !1
|
|
8313
|
-
}), [
|
|
8313
|
+
}), [Z, S, oe, le, E, j, z, Y, ee, L, R]);
|
|
8314
8314
|
const Q = T.current || {
|
|
8315
8315
|
avatarUrl: "/avatars/brunette.glb",
|
|
8316
8316
|
avatarBody: "F",
|
package/package.json
CHANGED
package/src/lib/talkinghead.mjs
CHANGED
|
@@ -1667,28 +1667,32 @@ class TalkingHead {
|
|
|
1667
1667
|
const tempEuler = new THREE.Euler();
|
|
1668
1668
|
const tempQuaternion = new THREE.Quaternion();
|
|
1669
1669
|
|
|
1670
|
-
//
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
//
|
|
1674
|
-
|
|
1675
|
-
|
|
1670
|
+
// Check if FBX animation is playing
|
|
1671
|
+
const isFBXPlaying = this.mixer && this.currentFBXAction && this.currentFBXAction.isRunning();
|
|
1672
|
+
|
|
1673
|
+
// Instead of forcing to a fixed value, reduce X rotation proportionally
|
|
1674
|
+
// This maintains the relative relationship between shoulders and arms
|
|
1675
|
+
// Natural relaxed shoulders: X rotation typically 0.3-0.6 radians
|
|
1676
|
+
// High shoulders: X rotation > 0.8 radians
|
|
1677
|
+
// We reduce high rotations by 40-50% to bring them down while maintaining arm relationships
|
|
1678
|
+
const reductionFactor = isFBXPlaying ? 0.5 : 0.6; // Reduce by 50% during FBX, 40% otherwise
|
|
1679
|
+
const maxAllowedX = 0.7; // Maximum allowed X rotation
|
|
1676
1680
|
|
|
1677
1681
|
// Adjust left shoulder bone directly
|
|
1678
1682
|
if (leftShoulderBone.quaternion) {
|
|
1679
1683
|
tempEuler.setFromQuaternion(leftShoulderBone.quaternion, 'XYZ');
|
|
1680
1684
|
const originalX = tempEuler.x;
|
|
1681
1685
|
|
|
1682
|
-
//
|
|
1683
|
-
if (tempEuler.x >
|
|
1684
|
-
//
|
|
1685
|
-
tempEuler.x =
|
|
1686
|
-
} else if (tempEuler.x >
|
|
1687
|
-
//
|
|
1688
|
-
tempEuler.x =
|
|
1686
|
+
// Reduce X rotation proportionally if it's too high
|
|
1687
|
+
if (tempEuler.x > maxAllowedX) {
|
|
1688
|
+
// Reduce by percentage to maintain arm-shoulder relationship
|
|
1689
|
+
tempEuler.x = maxAllowedX + (tempEuler.x - maxAllowedX) * (1 - reductionFactor);
|
|
1690
|
+
} else if (tempEuler.x > 0.5) {
|
|
1691
|
+
// Slight reduction for moderately high shoulders
|
|
1692
|
+
tempEuler.x = tempEuler.x * (1 - reductionFactor * 0.5);
|
|
1689
1693
|
}
|
|
1690
1694
|
|
|
1691
|
-
// Only update if we
|
|
1695
|
+
// Only update if we changed something significantly
|
|
1692
1696
|
if (Math.abs(tempEuler.x - originalX) > 0.01) {
|
|
1693
1697
|
tempQuaternion.setFromEuler(tempEuler, 'XYZ');
|
|
1694
1698
|
leftShoulderBone.quaternion.copy(tempQuaternion);
|
|
@@ -1701,16 +1705,16 @@ class TalkingHead {
|
|
|
1701
1705
|
tempEuler.setFromQuaternion(rightShoulderBone.quaternion, 'XYZ');
|
|
1702
1706
|
const originalX = tempEuler.x;
|
|
1703
1707
|
|
|
1704
|
-
//
|
|
1705
|
-
if (tempEuler.x >
|
|
1706
|
-
//
|
|
1707
|
-
tempEuler.x =
|
|
1708
|
-
} else if (tempEuler.x >
|
|
1709
|
-
//
|
|
1710
|
-
tempEuler.x =
|
|
1708
|
+
// Reduce X rotation proportionally if it's too high
|
|
1709
|
+
if (tempEuler.x > maxAllowedX) {
|
|
1710
|
+
// Reduce by percentage to maintain arm-shoulder relationship
|
|
1711
|
+
tempEuler.x = maxAllowedX + (tempEuler.x - maxAllowedX) * (1 - reductionFactor);
|
|
1712
|
+
} else if (tempEuler.x > 0.5) {
|
|
1713
|
+
// Slight reduction for moderately high shoulders
|
|
1714
|
+
tempEuler.x = tempEuler.x * (1 - reductionFactor * 0.5);
|
|
1711
1715
|
}
|
|
1712
1716
|
|
|
1713
|
-
// Only update if we
|
|
1717
|
+
// Only update if we changed something significantly
|
|
1714
1718
|
if (Math.abs(tempEuler.x - originalX) > 0.01) {
|
|
1715
1719
|
tempQuaternion.setFromEuler(tempEuler, 'XYZ');
|
|
1716
1720
|
rightShoulderBone.quaternion.copy(tempQuaternion);
|