@sage-rsc/talking-head-react 1.3.7 → 1.3.9
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 +1 -1
- package/dist/index.js +17 -4
- package/package.json +1 -1
- package/scripts/update-talkinghead.sh +51 -0
- package/src/lib/talkinghead.mjs +63 -4
- package/src/lib/talkinghead.mjs.backup +6242 -0
- package/src/lib/talkinghead.mjs.new +4825 -0
- package/src/lib/talkinghead.mjs.upstream +4825 -0
package/dist/index.js
CHANGED
|
@@ -4395,18 +4395,31 @@ class Be {
|
|
|
4395
4395
|
}
|
|
4396
4396
|
/**
|
|
4397
4397
|
* Apply shoulder adjustment to lower shoulders to a more natural position
|
|
4398
|
+
* This is called from updatePoseBase for pose-based animations
|
|
4398
4399
|
*/
|
|
4399
4400
|
applyShoulderAdjustment() {
|
|
4400
|
-
const
|
|
4401
|
+
const t = new y.Euler(), e = 1;
|
|
4401
4402
|
if (this.poseAvatar.props["LeftShoulder.quaternion"]) {
|
|
4402
4403
|
const n = this.poseAvatar.props["LeftShoulder.quaternion"];
|
|
4403
|
-
|
|
4404
|
+
t.setFromQuaternion(n, "XYZ"), t.x > e && (t.x = Math.max(e, t.x - 0.4)), n.setFromEuler(t, "XYZ");
|
|
4404
4405
|
}
|
|
4405
4406
|
if (this.poseAvatar.props["RightShoulder.quaternion"]) {
|
|
4406
4407
|
const n = this.poseAvatar.props["RightShoulder.quaternion"];
|
|
4407
|
-
|
|
4408
|
+
t.setFromQuaternion(n, "XYZ"), t.x > e && (t.x = Math.max(e, t.x - 0.4)), n.setFromEuler(t, "XYZ");
|
|
4408
4409
|
}
|
|
4409
4410
|
}
|
|
4411
|
+
/**
|
|
4412
|
+
* Apply shoulder adjustment directly to bone objects
|
|
4413
|
+
* This is called AFTER FBX animations update to ensure shoulders stay relaxed
|
|
4414
|
+
* regardless of what the animation sets
|
|
4415
|
+
*/
|
|
4416
|
+
applyShoulderAdjustmentToBones() {
|
|
4417
|
+
if (!this.armature) return;
|
|
4418
|
+
const t = this.armature.getObjectByName("LeftShoulder"), e = this.armature.getObjectByName("RightShoulder");
|
|
4419
|
+
if (!t || !e) return;
|
|
4420
|
+
const n = new y.Euler(), i = new y.Quaternion(), s = 1;
|
|
4421
|
+
t.quaternion && (n.setFromQuaternion(t.quaternion, "XYZ"), n.x > s && (n.x = Math.max(s, n.x - 0.4)), i.setFromEuler(n, "XYZ"), t.quaternion.copy(i), t.updateMatrixWorld(!0)), e.quaternion && (n.setFromQuaternion(e.quaternion, "XYZ"), n.x > s && (n.x = Math.max(s, n.x - 0.4)), i.setFromEuler(n, "XYZ"), e.quaternion.copy(i), e.updateMatrixWorld(!0));
|
|
4422
|
+
}
|
|
4410
4423
|
/**
|
|
4411
4424
|
* Update avatar pose deltas
|
|
4412
4425
|
*/
|
|
@@ -5208,7 +5221,7 @@ class Be {
|
|
|
5208
5221
|
eyeLookOutRight: [null, 0],
|
|
5209
5222
|
eyeContact: [0]
|
|
5210
5223
|
}
|
|
5211
|
-
})))), e > 2 * this.animFrameDur && (e = 2 * this.animFrameDur), (this.viewName !== "full" || this.isAvatarOnly) && (n = this.mtRandomized[Math.floor(Math.random() * this.mtRandomized.length)], i = this.mtAvatar[n], i.needsUpdate || Object.assign(i, { base: (this.mood.baseline[n] || 0) + (1 + l / 255) * Math.random() / 5, needsUpdate: !0 })), this.updatePoseBase(this.animClock), this.mixer && this.mixer.update(e / 1e3 * this.mixer.timeScale), this.updatePoseDelta(), (this.isSpeaking || this.isListening) && h ? l > this.volumeMax ? (this.volumeHeadBase = 0.05, Math.random() > 0.6 && (this.volumeHeadTarget = -0.05 - Math.random() / 15), this.volumeMax = l) : (this.volumeMax *= 0.92, this.volumeHeadTarget = this.volumeHeadBase - 0.9 * (this.volumeHeadBase - this.volumeHeadTarget)) : (this.volumeHeadTarget = 0, this.volumeMax = 0), n = this.volumeHeadTarget - this.volumeHeadCurrent, i = Math.abs(n), i > 1e-4 && (o = i * (this.volumeHeadEasing(Math.min(1, this.volumeHeadVelocity * e / 1e3 / i) / 2 + 0.5) - 0.5), this.volumeHeadCurrent += Math.sign(n) * Math.min(i, o)), Math.abs(this.volumeHeadCurrent) > 1e-4 && ($.setFromAxisAngle(mt, this.volumeHeadCurrent), this.objectNeck.quaternion.multiply($)), We.setFromObject(this.armature), this.objectLeftToeBase.getWorldPosition(ve), ve.sub(this.armature.position), this.objectRightToeBase.getWorldPosition(Re), Re.sub(this.armature.position), this.objectHips.position.y -= We.min.y / 2, this.objectHips.position.x -= (ve.x + Re.x) / 4, this.objectHips.position.z -= (ve.z + Re.z) / 2, this.dynamicbones.update(e), this.fbxAnimationLoader && this.fbxAnimationLoader.update(), this.opt.update && this.opt.update(e), this.updateMorphTargets(e), this.isAvatarOnly)
|
|
5224
|
+
})))), e > 2 * this.animFrameDur && (e = 2 * this.animFrameDur), (this.viewName !== "full" || this.isAvatarOnly) && (n = this.mtRandomized[Math.floor(Math.random() * this.mtRandomized.length)], i = this.mtAvatar[n], i.needsUpdate || Object.assign(i, { base: (this.mood.baseline[n] || 0) + (1 + l / 255) * Math.random() / 5, needsUpdate: !0 })), this.updatePoseBase(this.animClock), this.mixer && this.mixer.update(e / 1e3 * this.mixer.timeScale), this.updatePoseDelta(), (this.isSpeaking || this.isListening) && h ? l > this.volumeMax ? (this.volumeHeadBase = 0.05, Math.random() > 0.6 && (this.volumeHeadTarget = -0.05 - Math.random() / 15), this.volumeMax = l) : (this.volumeMax *= 0.92, this.volumeHeadTarget = this.volumeHeadBase - 0.9 * (this.volumeHeadBase - this.volumeHeadTarget)) : (this.volumeHeadTarget = 0, this.volumeMax = 0), n = this.volumeHeadTarget - this.volumeHeadCurrent, i = Math.abs(n), i > 1e-4 && (o = i * (this.volumeHeadEasing(Math.min(1, this.volumeHeadVelocity * e / 1e3 / i) / 2 + 0.5) - 0.5), this.volumeHeadCurrent += Math.sign(n) * Math.min(i, o)), Math.abs(this.volumeHeadCurrent) > 1e-4 && ($.setFromAxisAngle(mt, this.volumeHeadCurrent), this.objectNeck.quaternion.multiply($)), We.setFromObject(this.armature), this.objectLeftToeBase.getWorldPosition(ve), ve.sub(this.armature.position), this.objectRightToeBase.getWorldPosition(Re), Re.sub(this.armature.position), this.objectHips.position.y -= We.min.y / 2, this.objectHips.position.x -= (ve.x + Re.x) / 4, this.objectHips.position.z -= (ve.z + Re.z) / 2, this.dynamicbones.update(e), this.fbxAnimationLoader && this.fbxAnimationLoader.update(), this.applyShoulderAdjustmentToBones(), this.opt.update && this.opt.update(e), this.updateMorphTargets(e), this.isAvatarOnly)
|
|
5212
5225
|
this.stats && this.stats.end();
|
|
5213
5226
|
else {
|
|
5214
5227
|
if (this.cameraClock !== null && this.cameraClock < 1e3) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Script to help update TalkingHead while preserving customizations
|
|
3
|
+
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
echo "=== TalkingHead Update Helper ==="
|
|
7
|
+
echo ""
|
|
8
|
+
echo "This script helps identify customizations to preserve during update."
|
|
9
|
+
echo ""
|
|
10
|
+
|
|
11
|
+
# Colors for output
|
|
12
|
+
GREEN='\033[0;32m'
|
|
13
|
+
YELLOW='\033[1;33m'
|
|
14
|
+
NC='\033[0m' # No Color
|
|
15
|
+
|
|
16
|
+
echo -e "${GREEN}Step 1: Identifying custom methods...${NC}"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "=== Position Locking Methods ==="
|
|
19
|
+
grep -n "lockAvatarPosition\|unlockAvatarPosition\|maintainLockedPosition" src/lib/talkinghead.mjs | head -20
|
|
20
|
+
|
|
21
|
+
echo ""
|
|
22
|
+
echo "=== Shoulder Adjustment Methods ==="
|
|
23
|
+
grep -n "applyShoulderAdjustment" src/lib/talkinghead.mjs | head -10
|
|
24
|
+
|
|
25
|
+
echo ""
|
|
26
|
+
echo "=== Position Locking Calls ==="
|
|
27
|
+
grep -n "lockAvatarPosition\|unlockAvatarPosition\|positionWasLocked\|lockedPosition" src/lib/talkinghead.mjs | head -20
|
|
28
|
+
|
|
29
|
+
echo ""
|
|
30
|
+
echo -e "${YELLOW}Step 2: Custom files to preserve (DO NOT REPLACE):${NC}"
|
|
31
|
+
echo "- src/components/TalkingHeadComponent.jsx"
|
|
32
|
+
echo "- src/components/TalkingHeadAvatar.jsx"
|
|
33
|
+
echo "- src/components/SimpleTalkingAvatar.jsx"
|
|
34
|
+
echo "- src/components/CurriculumLearning.jsx"
|
|
35
|
+
echo "- src/config/ttsConfig.js"
|
|
36
|
+
echo "- src/lib/enhancedFBXLoader.js"
|
|
37
|
+
echo "- src/lib/fbxAnimationLoader.js"
|
|
38
|
+
echo "- src/utils/animationLoader.js"
|
|
39
|
+
echo "- scripts/generate-animation-manifest.js"
|
|
40
|
+
|
|
41
|
+
echo ""
|
|
42
|
+
echo -e "${GREEN}Step 3: Next steps:${NC}"
|
|
43
|
+
echo "1. Download latest TalkingHead source"
|
|
44
|
+
echo "2. Compare talkinghead.mjs files"
|
|
45
|
+
echo "3. Merge changes while preserving custom methods above"
|
|
46
|
+
echo "4. Test thoroughly"
|
|
47
|
+
echo ""
|
|
48
|
+
echo "To compare files, use:"
|
|
49
|
+
echo " diff -u old-talkinghead.mjs src/lib/talkinghead.mjs > talkinghead.diff"
|
|
50
|
+
echo ""
|
|
51
|
+
|
package/src/lib/talkinghead.mjs
CHANGED
|
@@ -1615,17 +1615,20 @@ class TalkingHead {
|
|
|
1615
1615
|
|
|
1616
1616
|
/**
|
|
1617
1617
|
* Apply shoulder adjustment to lower shoulders to a more natural position
|
|
1618
|
+
* This is called from updatePoseBase for pose-based animations
|
|
1618
1619
|
*/
|
|
1619
1620
|
applyShoulderAdjustment() {
|
|
1620
|
-
// Shoulder adjustment: reduce X-axis rotation by ~0.6 radians (34 degrees) to lower shoulders to a relaxed position
|
|
1621
|
-
const shoulderAdjustment = -0.6; // Negative to lower shoulders (increased for more relaxed look)
|
|
1622
1621
|
const tempEuler = new THREE.Euler();
|
|
1622
|
+
const maxX = 1.0; // Maximum X rotation for relaxed shoulders
|
|
1623
1623
|
|
|
1624
1624
|
// Adjust left shoulder
|
|
1625
1625
|
if (this.poseAvatar.props['LeftShoulder.quaternion']) {
|
|
1626
1626
|
const leftShoulder = this.poseAvatar.props['LeftShoulder.quaternion'];
|
|
1627
1627
|
tempEuler.setFromQuaternion(leftShoulder, 'XYZ');
|
|
1628
|
-
|
|
1628
|
+
// Reduce X rotation if too high for relaxed shoulders
|
|
1629
|
+
if (tempEuler.x > maxX) {
|
|
1630
|
+
tempEuler.x = Math.max(maxX, tempEuler.x - 0.4);
|
|
1631
|
+
}
|
|
1629
1632
|
leftShoulder.setFromEuler(tempEuler, 'XYZ');
|
|
1630
1633
|
}
|
|
1631
1634
|
|
|
@@ -1633,10 +1636,62 @@ class TalkingHead {
|
|
|
1633
1636
|
if (this.poseAvatar.props['RightShoulder.quaternion']) {
|
|
1634
1637
|
const rightShoulder = this.poseAvatar.props['RightShoulder.quaternion'];
|
|
1635
1638
|
tempEuler.setFromQuaternion(rightShoulder, 'XYZ');
|
|
1636
|
-
|
|
1639
|
+
// Reduce X rotation if too high for relaxed shoulders
|
|
1640
|
+
if (tempEuler.x > maxX) {
|
|
1641
|
+
tempEuler.x = Math.max(maxX, tempEuler.x - 0.4);
|
|
1642
|
+
}
|
|
1637
1643
|
rightShoulder.setFromEuler(tempEuler, 'XYZ');
|
|
1638
1644
|
}
|
|
1639
1645
|
}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* Apply shoulder adjustment directly to bone objects
|
|
1649
|
+
* This is called AFTER FBX animations update to ensure shoulders stay relaxed
|
|
1650
|
+
* regardless of what the animation sets
|
|
1651
|
+
*/
|
|
1652
|
+
applyShoulderAdjustmentToBones() {
|
|
1653
|
+
if (!this.armature) return;
|
|
1654
|
+
|
|
1655
|
+
// Get shoulder bones directly from armature
|
|
1656
|
+
const leftShoulderBone = this.armature.getObjectByName('LeftShoulder');
|
|
1657
|
+
const rightShoulderBone = this.armature.getObjectByName('RightShoulder');
|
|
1658
|
+
|
|
1659
|
+
if (!leftShoulderBone || !rightShoulderBone) return;
|
|
1660
|
+
|
|
1661
|
+
const tempEuler = new THREE.Euler();
|
|
1662
|
+
const tempQuaternion = new THREE.Quaternion();
|
|
1663
|
+
|
|
1664
|
+
// Target relaxed shoulder rotation for natural appearance
|
|
1665
|
+
// Reduce X rotation (forward tilt) to lower shoulders
|
|
1666
|
+
// Typical values: 1.5-1.8 (high/stiff) -> 0.9-1.1 (relaxed)
|
|
1667
|
+
const maxX = 1.0; // Maximum X rotation for relaxed shoulders
|
|
1668
|
+
|
|
1669
|
+
// Adjust left shoulder bone directly
|
|
1670
|
+
if (leftShoulderBone.quaternion) {
|
|
1671
|
+
tempEuler.setFromQuaternion(leftShoulderBone.quaternion, 'XYZ');
|
|
1672
|
+
// Reduce X rotation if it's too high for relaxed shoulders
|
|
1673
|
+
if (tempEuler.x > maxX) {
|
|
1674
|
+
// Smooth interpolation towards relaxed position
|
|
1675
|
+
tempEuler.x = Math.max(maxX, tempEuler.x - 0.4);
|
|
1676
|
+
}
|
|
1677
|
+
tempQuaternion.setFromEuler(tempEuler, 'XYZ');
|
|
1678
|
+
leftShoulderBone.quaternion.copy(tempQuaternion);
|
|
1679
|
+
leftShoulderBone.updateMatrixWorld(true);
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// Adjust right shoulder bone directly
|
|
1683
|
+
if (rightShoulderBone.quaternion) {
|
|
1684
|
+
tempEuler.setFromQuaternion(rightShoulderBone.quaternion, 'XYZ');
|
|
1685
|
+
// Reduce X rotation if it's too high for relaxed shoulders
|
|
1686
|
+
if (tempEuler.x > maxX) {
|
|
1687
|
+
// Smooth interpolation towards relaxed position
|
|
1688
|
+
tempEuler.x = Math.max(maxX, tempEuler.x - 0.4);
|
|
1689
|
+
}
|
|
1690
|
+
tempQuaternion.setFromEuler(tempEuler, 'XYZ');
|
|
1691
|
+
rightShoulderBone.quaternion.copy(tempQuaternion);
|
|
1692
|
+
rightShoulderBone.updateMatrixWorld(true);
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1640
1695
|
|
|
1641
1696
|
/**
|
|
1642
1697
|
* Update avatar pose deltas
|
|
@@ -3221,6 +3276,10 @@ class TalkingHead {
|
|
|
3221
3276
|
if (this.fbxAnimationLoader) {
|
|
3222
3277
|
this.fbxAnimationLoader.update();
|
|
3223
3278
|
}
|
|
3279
|
+
|
|
3280
|
+
// Apply shoulder adjustment AFTER FBX animations to ensure relaxed shoulders
|
|
3281
|
+
// This overrides any shoulder positions set by animations
|
|
3282
|
+
this.applyShoulderAdjustmentToBones();
|
|
3224
3283
|
|
|
3225
3284
|
// Custom update
|
|
3226
3285
|
if ( this.opt.update ) {
|