@sage-rsc/talking-head-react 1.4.9 → 1.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.4.9",
3
+ "version": "1.5.1",
4
4
  "description": "A reusable React component for 3D talking avatars with lip-sync and text-to-speech",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -1667,66 +1667,55 @@ class TalkingHead {
1667
1667
  const tempEuler = new THREE.Euler();
1668
1668
  const tempQuaternion = new THREE.Quaternion();
1669
1669
 
1670
- // Check if FBX animation is playing - if so, be even more aggressive
1671
- const isFBXPlaying = this.mixer && this.currentFBXAction && this.currentFBXAction.isRunning();
1672
-
1673
1670
  // Research-based relaxed shoulder rotation values
1674
- // Natural relaxed shoulders: X rotation ~0.4-0.5 radians
1671
+ // Natural relaxed shoulders: X rotation ~0.5-0.7 radians (much lower for natural look)
1675
1672
  // High/stiff shoulders: X rotation ~1.5-1.8 radians
1676
- // We ALWAYS clamp to relaxed position, especially during FBX animations
1677
- const targetX = 0.5; // Target X rotation for relaxed, natural shoulders (radians)
1678
- const maxX = 0.6; // Maximum allowed X rotation - very strict
1673
+ // We aggressively clamp to a very relaxed position
1674
+ const targetX = 0.6; // Target X rotation for relaxed, natural shoulders (radians) - lowered significantly
1675
+ const maxX = 0.7; // Maximum allowed X rotation - lowered significantly
1679
1676
 
1680
1677
  // Adjust left shoulder bone directly
1681
1678
  if (leftShoulderBone.quaternion) {
1682
1679
  tempEuler.setFromQuaternion(leftShoulderBone.quaternion, 'XYZ');
1680
+ const originalX = tempEuler.x;
1683
1681
 
1684
- // ALWAYS clamp X rotation to relaxed position (no threshold check)
1685
- // During FBX animations, be even more aggressive
1682
+ // Aggressively clamp X rotation to relaxed position
1686
1683
  if (tempEuler.x > maxX) {
1687
- // Force to target relaxed position immediately
1684
+ // Force to target relaxed position
1688
1685
  tempEuler.x = targetX;
1689
1686
  } else if (tempEuler.x > targetX) {
1690
- // If slightly above target, reduce aggressively
1691
- if (isFBXPlaying) {
1692
- // During FBX animations, force to target immediately
1693
- tempEuler.x = targetX;
1694
- } else {
1695
- // Otherwise, reduce smoothly but still aggressively
1696
- tempEuler.x = targetX + (tempEuler.x - targetX) * 0.1;
1697
- }
1687
+ // Smoothly reduce if slightly above target
1688
+ tempEuler.x = targetX + (tempEuler.x - targetX) * 0.2; // More aggressive reduction
1698
1689
  }
1699
1690
 
1700
- // Always apply the adjustment (no change detection)
1701
- tempQuaternion.setFromEuler(tempEuler, 'XYZ');
1702
- leftShoulderBone.quaternion.copy(tempQuaternion);
1703
- leftShoulderBone.updateMatrixWorld(true);
1691
+ // Only update if we actually changed something
1692
+ if (Math.abs(tempEuler.x - originalX) > 0.01) {
1693
+ tempQuaternion.setFromEuler(tempEuler, 'XYZ');
1694
+ leftShoulderBone.quaternion.copy(tempQuaternion);
1695
+ leftShoulderBone.updateMatrixWorld(true);
1696
+ }
1704
1697
  }
1705
1698
 
1706
1699
  // Adjust right shoulder bone directly
1707
1700
  if (rightShoulderBone.quaternion) {
1708
1701
  tempEuler.setFromQuaternion(rightShoulderBone.quaternion, 'XYZ');
1702
+ const originalX = tempEuler.x;
1709
1703
 
1710
- // ALWAYS clamp X rotation to relaxed position (no threshold check)
1711
- // During FBX animations, be even more aggressive
1704
+ // Aggressively clamp X rotation to relaxed position
1712
1705
  if (tempEuler.x > maxX) {
1713
- // Force to target relaxed position immediately
1706
+ // Force to target relaxed position
1714
1707
  tempEuler.x = targetX;
1715
1708
  } else if (tempEuler.x > targetX) {
1716
- // If slightly above target, reduce aggressively
1717
- if (isFBXPlaying) {
1718
- // During FBX animations, force to target immediately
1719
- tempEuler.x = targetX;
1720
- } else {
1721
- // Otherwise, reduce smoothly but still aggressively
1722
- tempEuler.x = targetX + (tempEuler.x - targetX) * 0.1;
1723
- }
1709
+ // Smoothly reduce if slightly above target
1710
+ tempEuler.x = targetX + (tempEuler.x - targetX) * 0.2; // More aggressive reduction
1724
1711
  }
1725
1712
 
1726
- // Always apply the adjustment (no change detection)
1727
- tempQuaternion.setFromEuler(tempEuler, 'XYZ');
1728
- rightShoulderBone.quaternion.copy(tempQuaternion);
1729
- rightShoulderBone.updateMatrixWorld(true);
1713
+ // Only update if we actually changed something
1714
+ if (Math.abs(tempEuler.x - originalX) > 0.01) {
1715
+ tempQuaternion.setFromEuler(tempEuler, 'XYZ');
1716
+ rightShoulderBone.quaternion.copy(tempQuaternion);
1717
+ rightShoulderBone.updateMatrixWorld(true);
1718
+ }
1730
1719
  }
1731
1720
  }
1732
1721
 
@@ -5771,6 +5760,7 @@ class TalkingHead {
5771
5760
  // Filter and map animation tracks
5772
5761
  const mappedTracks = [];
5773
5762
  const unmappedBones = new Set();
5763
+ let filteredShoulderTracks = 0;
5774
5764
  anim.tracks.forEach(track => {
5775
5765
  // Remove mixamorig prefix first
5776
5766
  let trackName = track.name.replaceAll('mixamorig', '');
@@ -5781,8 +5771,13 @@ class TalkingHead {
5781
5771
  // Map bone name to avatar skeleton
5782
5772
  const mappedBoneName = mapBoneName(fbxBoneName);
5783
5773
 
5784
- // Note: We allow shoulder rotation tracks to play so arms position correctly
5785
- // The shoulder adjustment function will override them after animation updates
5774
+ // Filter out shoulder rotation tracks - we'll control shoulders manually
5775
+ if (mappedBoneName && (mappedBoneName === 'LeftShoulder' || mappedBoneName === 'RightShoulder')) {
5776
+ if (property === 'quaternion' || property === 'rotation') {
5777
+ filteredShoulderTracks++;
5778
+ return; // Skip this track - don't add it to mappedTracks
5779
+ }
5780
+ }
5786
5781
 
5787
5782
  if (mappedBoneName && property) {
5788
5783
  // Create new track with mapped bone name
@@ -5811,6 +5806,10 @@ class TalkingHead {
5811
5806
  }
5812
5807
  });
5813
5808
 
5809
+ if (filteredShoulderTracks > 0) {
5810
+ console.log(`✓ Filtered out ${filteredShoulderTracks} shoulder rotation track(s) to prevent high shoulders`);
5811
+ }
5812
+
5814
5813
  if (unmappedBones.size > 0) {
5815
5814
  console.warn(`⚠️ ${unmappedBones.size} bone(s) could not be mapped:`, Array.from(unmappedBones).sort().join(', '));
5816
5815
  }