@sage-rsc/talking-head-react 1.1.3 → 1.1.5

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.1.3",
3
+ "version": "1.1.5",
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",
@@ -5419,6 +5419,9 @@ class TalkingHead {
5419
5419
  'L_Middle1': 'LeftHandMiddle1',
5420
5420
  'L_Middle2': 'LeftHandMiddle2',
5421
5421
  'L_Middle3': 'LeftHandMiddle3',
5422
+ 'L_Mid1': 'LeftHandMiddle1',
5423
+ 'L_Mid2': 'LeftHandMiddle2',
5424
+ 'L_Mid3': 'LeftHandMiddle3',
5422
5425
  'L_Ring1': 'LeftHandRing1',
5423
5426
  'L_Ring2': 'LeftHandRing2',
5424
5427
  'L_Ring3': 'LeftHandRing3',
@@ -5440,6 +5443,9 @@ class TalkingHead {
5440
5443
  'R_Middle1': 'RightHandMiddle1',
5441
5444
  'R_Middle2': 'RightHandMiddle2',
5442
5445
  'R_Middle3': 'RightHandMiddle3',
5446
+ 'R_Mid1': 'RightHandMiddle1',
5447
+ 'R_Mid2': 'RightHandMiddle2',
5448
+ 'R_Mid3': 'RightHandMiddle3',
5443
5449
  'R_Ring1': 'RightHandRing1',
5444
5450
  'R_Ring2': 'RightHandRing2',
5445
5451
  'R_Ring3': 'RightHandRing3',
@@ -5504,8 +5510,8 @@ class TalkingHead {
5504
5510
  }
5505
5511
  }
5506
5512
 
5507
- // Pattern: R_Middle1/2/3 -> RightHandMiddle1/2/3
5508
- const middleMatch = lowerNormalized.match(/^[rl]_middle(\d+)$/);
5513
+ // Pattern: R_Middle1/2/3 or R_Mid1/2/3 -> RightHandMiddle1/2/3
5514
+ const middleMatch = lowerNormalized.match(/^[rl]_(?:middle|mid)(\d+)$/);
5509
5515
  if (middleMatch) {
5510
5516
  const digit = middleMatch[1];
5511
5517
  const side = lowerNormalized.startsWith('r') ? 'Right' : 'Left';
@@ -5535,8 +5541,12 @@ class TalkingHead {
5535
5541
  }
5536
5542
  }
5537
5543
 
5538
- // Pattern: R_UpperarmTwist01/02 -> ignore (twist bones)
5539
- if (lowerNormalized.includes('upperarmtwist') || lowerNormalized.includes('forearmtwist')) {
5544
+ // Pattern: R_UpperarmTwist01/02 -> ignore (twist bones and other extra bones)
5545
+ if (lowerNormalized.includes('upperarmtwist') ||
5546
+ lowerNormalized.includes('forearmtwist') ||
5547
+ lowerNormalized.includes('ribstwist') ||
5548
+ lowerNormalized.includes('breast') ||
5549
+ lowerNormalized.includes('twist')) {
5540
5550
  return null;
5541
5551
  }
5542
5552
 
@@ -5604,6 +5614,72 @@ class TalkingHead {
5604
5614
  const newTrack = track.clone();
5605
5615
  newTrack.name = newTrackName;
5606
5616
 
5617
+ // Fix rotations for arm/hand bones that might be inverted
5618
+ // If hands are folding behind instead of in front, we need to adjust rotations
5619
+ const isArmBone = mappedBoneName.includes('Arm') || mappedBoneName.includes('Hand') || mappedBoneName.includes('Shoulder');
5620
+ const isForearmBone = mappedBoneName.includes('ForeArm');
5621
+
5622
+ if (isArmBone && (property === 'quaternion' || property === 'rotation')) {
5623
+ // For quaternion tracks, we might need to adjust the rotation
5624
+ // Check if this is a quaternion track
5625
+ if (property === 'quaternion' && newTrack.values && newTrack.values.length >= 4) {
5626
+ // Quaternion format: [x, y, z, w] per keyframe
5627
+ // For arm bones, we might need to invert Y or Z rotation
5628
+ // Adjust quaternion values to fix hand position
5629
+ const numKeyframes = newTrack.times.length;
5630
+ for (let i = 0; i < numKeyframes; i++) {
5631
+ const baseIdx = i * 4;
5632
+ if (baseIdx + 3 < newTrack.values.length) {
5633
+ // Get quaternion values
5634
+ let x = newTrack.values[baseIdx];
5635
+ let y = newTrack.values[baseIdx + 1];
5636
+ let z = newTrack.values[baseIdx + 2];
5637
+ let w = newTrack.values[baseIdx + 3];
5638
+
5639
+ // For arms, adjust rotation to flip hands from back to front
5640
+ // This is a common fix for FBX animations with different coordinate systems
5641
+ if (isForearmBone || mappedBoneName.includes('Hand')) {
5642
+ // Rotate 180 degrees around X axis to flip hands from behind to in front
5643
+ // Create a 180-degree rotation around X axis
5644
+ const flipAngle = Math.PI;
5645
+ const flipX = Math.cos(flipAngle / 2); // w component
5646
+ const flipY = Math.sin(flipAngle / 2); // x component (axis X = 1,0,0)
5647
+
5648
+ // Multiply quaternions: q_result = q_flip * q_original
5649
+ // For rotation around X axis: q_flip = (sin(θ/2), 0, 0, cos(θ/2))
5650
+ const qw = flipX * w - flipY * x;
5651
+ const qx = flipX * x + flipY * w;
5652
+ const qy = flipX * y - flipY * z;
5653
+ const qz = flipX * z + flipY * y;
5654
+
5655
+ x = qx;
5656
+ y = qy;
5657
+ z = qz;
5658
+ w = qw;
5659
+ }
5660
+
5661
+ newTrack.values[baseIdx] = x;
5662
+ newTrack.values[baseIdx + 1] = y;
5663
+ newTrack.values[baseIdx + 2] = z;
5664
+ newTrack.values[baseIdx + 3] = w;
5665
+ }
5666
+ }
5667
+ } else if (property === 'rotation' && newTrack.values && newTrack.values.length >= 3) {
5668
+ // Euler rotation format: [x, y, z] per keyframe
5669
+ const numKeyframes = newTrack.times.length;
5670
+ for (let i = 0; i < numKeyframes; i++) {
5671
+ const baseIdx = i * 3;
5672
+ if (baseIdx + 2 < newTrack.values.length) {
5673
+ // For arm bones, adjust Y rotation to flip hands
5674
+ if (isForearmBone || mappedBoneName.includes('Hand')) {
5675
+ // Add 180 degrees (PI radians) to Y rotation
5676
+ newTrack.values[baseIdx + 1] += Math.PI;
5677
+ }
5678
+ }
5679
+ }
5680
+ }
5681
+ }
5682
+
5607
5683
  validTracks.push(newTrack);
5608
5684
 
5609
5685
  // Track mappings for logging