@sage-rsc/talking-head-react 1.4.1 → 1.4.3

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.js CHANGED
@@ -4016,7 +4016,7 @@ class Ne {
4016
4016
  default:
4017
4017
  r += 12, u = u * r;
4018
4018
  }
4019
- a = a * r, this.controlsEnd = new g.Vector3(a, u, 0), this.cameraEnd = new g.Vector3(a, u, r).applyEuler(new g.Euler(o, l, 0)), this.cameraClock === null && (this.controls.target.copy(this.controlsEnd), this.camera.position.copy(this.cameraEnd)), this.controlsStart = this.controls.target.clone(), this.cameraStart = this.camera.position.clone(), this.cameraClock = 0;
4019
+ a = a * r, this.controlsEnd = new g.Vector3(a, u, 0), this.cameraEnd = new g.Vector3(a, u, r).applyEuler(new g.Euler(o, l, 0)), this.controls && (this.cameraClock === null && (this.controls.target.copy(this.controlsEnd), this.camera.position.copy(this.cameraEnd)), this.controlsStart = this.controls.target.clone(), this.cameraStart = this.camera.position.clone(), this.cameraClock = 0);
4020
4020
  }
4021
4021
  /**
4022
4022
  * Change light colors and intensities.
@@ -4035,7 +4035,7 @@ class Ne {
4035
4035
  * Resize avatar.
4036
4036
  */
4037
4037
  onResize() {
4038
- this.isAvatarOnly || (this.camera.aspect = this.nodeAvatar.clientWidth / this.nodeAvatar.clientHeight, this.camera.updateProjectionMatrix(), this.renderer.setSize(this.nodeAvatar.clientWidth, this.nodeAvatar.clientHeight), this.controls.update(), this.render());
4038
+ this.isAvatarOnly || (this.camera.aspect = this.nodeAvatar.clientWidth / this.nodeAvatar.clientHeight, this.camera.updateProjectionMatrix(), this.renderer.setSize(this.nodeAvatar.clientWidth, this.nodeAvatar.clientHeight), this.controls && this.controls.update(), this.render());
4039
4039
  }
4040
4040
  /**
4041
4041
  * Update avatar pose.
@@ -4240,8 +4240,9 @@ class Ne {
4240
4240
  if ((!i && !s || i && s) && (this.poseTarget.props = this.mirrorPose(this.poseTarget.props), this.poseWeightOnLeft = !this.poseWeightOnLeft), this.gesture)
4241
4241
  for (let [l, h] of Object.entries(this.gesture))
4242
4242
  this.poseTarget.props.hasOwnProperty(l) && (this.poseTarget.props[l].copy(h), this.poseTarget.props[l].t = h.t, this.poseTarget.props[l].d = h.d);
4243
- this.poseBase && this.poseBase.props && this.poseDelta && this.poseDelta.props && Object.keys(this.poseDelta.props).forEach((l) => {
4244
- !this.poseTarget.props.hasOwnProperty(l) && this.poseBase.props[l] && (this.poseTarget.props[l] = this.poseBase.props[l].clone(), this.poseTarget.props[l].t = this.animClock, this.poseTarget.props[l].d = o);
4243
+ this.poseBase && this.poseBase.props && this.poseDelta && this.poseDelta.props && this.poseTarget && this.poseTarget.props && Object.keys(this.poseDelta.props).forEach((l) => {
4244
+ const h = this.poseBase.props[l];
4245
+ !this.poseTarget.props.hasOwnProperty(l) && h && typeof h == "object" && typeof h.clone == "function" && (this.poseTarget.props[l] = h.clone(), this.poseTarget.props[l].t = this.animClock, this.poseTarget.props[l].d = o);
4245
4246
  });
4246
4247
  }
4247
4248
  }
@@ -4589,12 +4590,12 @@ class Ne {
4589
4590
  })))), e > 2 * this.animFrameDur && (e = 2 * this.animFrameDur), (this.viewName !== "full" || this.isAvatarOnly) && (t = this.mtRandomized[Math.floor(Math.random() * this.mtRandomized.length)], i = this.mtAvatar[t], i.needsUpdate || Object.assign(i, { base: (this.mood.baseline[t] || 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), t = this.volumeHeadTarget - this.volumeHeadCurrent, i = Math.abs(t), i > 1e-4 && (o = i * (this.volumeHeadEasing(Math.min(1, this.volumeHeadVelocity * e / 1e3 / i) / 2 + 0.5) - 0.5), this.volumeHeadCurrent += Math.sign(t) * Math.min(i, o)), Math.abs(this.volumeHeadCurrent) > 1e-4 && (K.setFromAxisAngle(dt, this.volumeHeadCurrent), this.objectNeck.quaternion.multiply(K)), We.setFromObject(this.armature), this.objectLeftToeBase.getWorldPosition(Re), Re.sub(this.armature.position), this.objectRightToeBase.getWorldPosition(Ie), Ie.sub(this.armature.position), this.objectHips.position.y -= We.min.y / 2, this.objectHips.position.x -= (Re.x + Ie.x) / 4, this.objectHips.position.z -= (Re.z + Ie.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)
4590
4591
  this.stats && this.stats.end();
4591
4592
  else {
4592
- if (this.cameraClock !== null && this.cameraClock < 1e3) {
4593
+ if (this.controls && this.cameraClock !== null && this.cameraClock < 1e3) {
4593
4594
  this.cameraClock += e, this.cameraClock > 1e3 && (this.cameraClock = 1e3);
4594
4595
  let r = new g.Spherical().setFromVector3(this.cameraStart), d = new g.Spherical().setFromVector3(this.cameraEnd);
4595
4596
  r.phi += this.easing(this.cameraClock / 1e3) * (d.phi - r.phi), r.theta += this.easing(this.cameraClock / 1e3) * (d.theta - r.theta), r.radius += this.easing(this.cameraClock / 1e3) * (d.radius - r.radius), r.makeSafe(), this.camera.position.setFromSpherical(r), this.controlsStart.x !== this.controlsEnd.x ? this.controls.target.copy(this.controlsStart.lerp(this.controlsEnd, this.easing(this.cameraClock / 1e3))) : (r.setFromVector3(this.controlsStart), d.setFromVector3(this.controlsEnd), r.phi += this.easing(this.cameraClock / 1e3) * (d.phi - r.phi), r.theta += this.easing(this.cameraClock / 1e3) * (d.theta - r.theta), r.radius += this.easing(this.cameraClock / 1e3) * (d.radius - r.radius), r.makeSafe(), this.controls.target.setFromSpherical(r)), this.controls.update();
4596
4597
  }
4597
- this.controls.autoRotate && this.controls.update(), this.stats && this.stats.end(), this.render();
4598
+ this.controls && this.controls.autoRotate && this.controls.update(), this.stats && this.stats.end(), this.render();
4598
4599
  }
4599
4600
  }
4600
4601
  /**
@@ -5431,14 +5432,14 @@ class Ne {
5431
5432
  * @return {numeric} Autorotate speed.
5432
5433
  */
5433
5434
  getAutoRotateSpeed(n) {
5434
- return this.controls.autoRotateSpeed;
5435
+ return this.controls ? this.controls.autoRotateSpeed : 0;
5435
5436
  }
5436
5437
  /**
5437
5438
  * Set autorotate.
5438
5439
  * @param {numeric} speed Autorotate speed, e.g. value 2 = 30 secs per orbit at 60fps.
5439
5440
  */
5440
5441
  setAutoRotateSpeed(n) {
5441
- this.controls.autoRotateSpeed = n, this.controls.autoRotate = n > 0;
5442
+ this.controls && (this.controls.autoRotateSpeed = n, this.controls.autoRotate = n > 0);
5442
5443
  }
5443
5444
  /**
5444
5445
  * Start animation cycle.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sage-rsc/talking-head-react",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
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",
@@ -1512,6 +1512,11 @@ class TalkingHead {
1512
1512
  this.controlsEnd = new THREE.Vector3(x, y, 0);
1513
1513
  this.cameraEnd = new THREE.Vector3(x, y, z).applyEuler( new THREE.Euler( cameraRotateX, cameraRotateY, 0 ) );
1514
1514
 
1515
+ // Guard against null controls (e.g., in avatarOnly mode or before initialization)
1516
+ if ( !this.controls ) {
1517
+ return;
1518
+ }
1519
+
1515
1520
  if ( this.cameraClock === null ) {
1516
1521
  this.controls.target.copy( this.controlsEnd );
1517
1522
  this.camera.position.copy( this.cameraEnd );
@@ -1585,7 +1590,9 @@ class TalkingHead {
1585
1590
  this.camera.aspect = this.nodeAvatar.clientWidth / this.nodeAvatar.clientHeight;
1586
1591
  this.camera.updateProjectionMatrix();
1587
1592
  this.renderer.setSize( this.nodeAvatar.clientWidth, this.nodeAvatar.clientHeight );
1593
+ if ( this.controls ) {
1588
1594
  this.controls.update();
1595
+ }
1589
1596
  this.render();
1590
1597
  }
1591
1598
  }
@@ -2089,10 +2096,12 @@ class TalkingHead {
2089
2096
 
2090
2097
  // Make sure deltas are included in the target
2091
2098
  // Guard against disposal: check if poseBase and its props exist
2092
- if (this.poseBase && this.poseBase.props && this.poseDelta && this.poseDelta.props) {
2099
+ if (this.poseBase && this.poseBase.props && this.poseDelta && this.poseDelta.props && this.poseTarget && this.poseTarget.props) {
2093
2100
  Object.keys(this.poseDelta.props).forEach( key => {
2094
- if ( !this.poseTarget.props.hasOwnProperty(key) && this.poseBase.props[key] ) {
2095
- this.poseTarget.props[key] = this.poseBase.props[key].clone();
2101
+ const baseProp = this.poseBase.props[key];
2102
+ // Ensure baseProp exists, is an object, and has a clone method
2103
+ if ( !this.poseTarget.props.hasOwnProperty(key) && baseProp && typeof baseProp === 'object' && typeof baseProp.clone === 'function' ) {
2104
+ this.poseTarget.props[key] = baseProp.clone();
2096
2105
  this.poseTarget.props[key].t = this.animClock;
2097
2106
  this.poseTarget.props[key].d = duration;
2098
2107
  }
@@ -2829,7 +2838,7 @@ class TalkingHead {
2829
2838
  } else {
2830
2839
 
2831
2840
  // Camera
2832
- if ( this.cameraClock !== null && this.cameraClock < 1000 ) {
2841
+ if ( this.controls && this.cameraClock !== null && this.cameraClock < 1000 ) {
2833
2842
  this.cameraClock += dt;
2834
2843
  if ( this.cameraClock > 1000 ) this.cameraClock = 1000;
2835
2844
  let s = new THREE.Spherical().setFromVector3(this.cameraStart);
@@ -2854,7 +2863,7 @@ class TalkingHead {
2854
2863
  }
2855
2864
 
2856
2865
  // Autorotate
2857
- if ( this.controls.autoRotate ) this.controls.update();
2866
+ if ( this.controls && this.controls.autoRotate ) this.controls.update();
2858
2867
 
2859
2868
  // Statistics end
2860
2869
  if ( this.stats ) {
@@ -2890,7 +2899,7 @@ class TalkingHead {
2890
2899
  const langLower = lang.toLowerCase();
2891
2900
  // Use statically imported modules from LIPSYNC_MODULES
2892
2901
  if (LIPSYNC_MODULES[langLower]) {
2893
- const className = 'Lipsync' + lang.charAt(0).toUpperCase() + lang.slice(1);
2902
+ const className = 'Lipsync' + lang.charAt(0).toUpperCase() + lang.slice(1);
2894
2903
  if (LIPSYNC_MODULES[langLower][className]) {
2895
2904
  this.lipsync[lang] = new LIPSYNC_MODULES[langLower][className];
2896
2905
  } else {
@@ -4358,7 +4367,7 @@ class TalkingHead {
4358
4367
  * @return {numeric} Autorotate speed.
4359
4368
  */
4360
4369
  getAutoRotateSpeed(k) {
4361
- return this.controls.autoRotateSpeed;
4370
+ return this.controls ? this.controls.autoRotateSpeed : 0;
4362
4371
  }
4363
4372
 
4364
4373
  /**
@@ -4366,8 +4375,10 @@ class TalkingHead {
4366
4375
  * @param {numeric} speed Autorotate speed, e.g. value 2 = 30 secs per orbit at 60fps.
4367
4376
  */
4368
4377
  setAutoRotateSpeed(speed) {
4378
+ if ( this.controls ) {
4369
4379
  this.controls.autoRotateSpeed = speed;
4370
4380
  this.controls.autoRotate = (speed > 0);
4381
+ }
4371
4382
  }
4372
4383
 
4373
4384
  /**