@sage-rsc/talking-head-react 1.4.7 → 1.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
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,55 +1667,66 @@ 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
+
1670
1673
  // Research-based relaxed shoulder rotation values
1671
- // Natural relaxed shoulders: X rotation ~0.5-0.7 radians (much lower for natural look)
1674
+ // Natural relaxed shoulders: X rotation ~0.4-0.5 radians
1672
1675
  // High/stiff shoulders: X rotation ~1.5-1.8 radians
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
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
1676
1679
 
1677
1680
  // Adjust left shoulder bone directly
1678
1681
  if (leftShoulderBone.quaternion) {
1679
1682
  tempEuler.setFromQuaternion(leftShoulderBone.quaternion, 'XYZ');
1680
- const originalX = tempEuler.x;
1681
1683
 
1682
- // Aggressively clamp X rotation to relaxed position
1684
+ // ALWAYS clamp X rotation to relaxed position (no threshold check)
1685
+ // During FBX animations, be even more aggressive
1683
1686
  if (tempEuler.x > maxX) {
1684
- // Force to target relaxed position
1687
+ // Force to target relaxed position immediately
1685
1688
  tempEuler.x = targetX;
1686
1689
  } else if (tempEuler.x > targetX) {
1687
- // Smoothly reduce if slightly above target
1688
- tempEuler.x = targetX + (tempEuler.x - targetX) * 0.2; // More aggressive reduction
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
+ }
1689
1698
  }
1690
1699
 
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
- }
1700
+ // Always apply the adjustment (no change detection)
1701
+ tempQuaternion.setFromEuler(tempEuler, 'XYZ');
1702
+ leftShoulderBone.quaternion.copy(tempQuaternion);
1703
+ leftShoulderBone.updateMatrixWorld(true);
1697
1704
  }
1698
1705
 
1699
1706
  // Adjust right shoulder bone directly
1700
1707
  if (rightShoulderBone.quaternion) {
1701
1708
  tempEuler.setFromQuaternion(rightShoulderBone.quaternion, 'XYZ');
1702
- const originalX = tempEuler.x;
1703
1709
 
1704
- // Aggressively clamp X rotation to relaxed position
1710
+ // ALWAYS clamp X rotation to relaxed position (no threshold check)
1711
+ // During FBX animations, be even more aggressive
1705
1712
  if (tempEuler.x > maxX) {
1706
- // Force to target relaxed position
1713
+ // Force to target relaxed position immediately
1707
1714
  tempEuler.x = targetX;
1708
1715
  } else if (tempEuler.x > targetX) {
1709
- // Smoothly reduce if slightly above target
1710
- tempEuler.x = targetX + (tempEuler.x - targetX) * 0.2; // More aggressive reduction
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
+ }
1711
1724
  }
1712
1725
 
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
- }
1726
+ // Always apply the adjustment (no change detection)
1727
+ tempQuaternion.setFromEuler(tempEuler, 'XYZ');
1728
+ rightShoulderBone.quaternion.copy(tempQuaternion);
1729
+ rightShoulderBone.updateMatrixWorld(true);
1719
1730
  }
1720
1731
  }
1721
1732
 
@@ -5760,7 +5771,6 @@ class TalkingHead {
5760
5771
  // Filter and map animation tracks
5761
5772
  const mappedTracks = [];
5762
5773
  const unmappedBones = new Set();
5763
- let filteredShoulderTracks = 0;
5764
5774
  anim.tracks.forEach(track => {
5765
5775
  // Remove mixamorig prefix first
5766
5776
  let trackName = track.name.replaceAll('mixamorig', '');
@@ -5771,13 +5781,8 @@ class TalkingHead {
5771
5781
  // Map bone name to avatar skeleton
5772
5782
  const mappedBoneName = mapBoneName(fbxBoneName);
5773
5783
 
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
- }
5784
+ // Note: We allow shoulder rotation tracks to play so arms position correctly
5785
+ // The shoulder adjustment function will override them after animation updates
5781
5786
 
5782
5787
  if (mappedBoneName && property) {
5783
5788
  // Create new track with mapped bone name
@@ -5806,10 +5811,6 @@ class TalkingHead {
5806
5811
  }
5807
5812
  });
5808
5813
 
5809
- if (filteredShoulderTracks > 0) {
5810
- console.log(`✓ Filtered out ${filteredShoulderTracks} shoulder rotation track(s) to prevent high shoulders`);
5811
- }
5812
-
5813
5814
  if (unmappedBones.size > 0) {
5814
5815
  console.warn(`⚠️ ${unmappedBones.size} bone(s) could not be mapped:`, Array.from(unmappedBones).sort().join(', '));
5815
5816
  }