@meimu/mutracer 0.1.0 → 0.1.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.
@@ -936,6 +936,24 @@ function convertScene(scene, options, store) {
936
936
  lights
937
937
  };
938
938
  }
939
+ /** Inputs that can wake an idle OrbitControls instance, without inspecting its private inertia. */
940
+ function orbitControlsState(camera, controls) {
941
+ if (!camera || !controls.getPolarAngle || !controls.getAzimuthalAngle)
942
+ return;
943
+ camera.parent?.updateWorldMatrix(true, false);
944
+ const state = [camera, controls.update, camera.parent, ...camera.position.toArray(), ...camera.quaternion.toArray(),
945
+ ...camera.scale.toArray(), ...camera.up.toArray(), camera.zoom, ...(camera.parent?.matrixWorld.elements ?? [])];
946
+ for (const [key, value] of Object.entries(controls)) {
947
+ if (key.startsWith('_'))
948
+ continue;
949
+ const vector = value;
950
+ if (vector?.isVector3)
951
+ state.push(key, ...vector.toArray());
952
+ else if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'string')
953
+ state.push(key, value);
954
+ }
955
+ return state;
956
+ }
939
957
  /**
940
958
  * Keeps one MuTracer scene in sync with a Three.js scene, reusing unchanged objects.
941
959
  * Skinning/morph targets run on the CPU with per-source-mesh dynamic geometry.
@@ -955,7 +973,8 @@ export class THREESync extends Sync {
955
973
  _started = false;
956
974
  _cameraState = [];
957
975
  _lastUpdate;
958
- _start = () => { this._started = true; this.setActive(true); };
976
+ _idleControlsState;
977
+ _start = () => { this._started = true; this._idleControlsState = undefined; this.setActive(true); };
959
978
  _end = () => { this._started = false; };
960
979
  /** Subscribes to controls without converting sources or modifying the engine. */
961
980
  constructor(engine, _options = {}) {
@@ -971,25 +990,53 @@ export class THREESync extends Sync {
971
990
  if (this._disposed)
972
991
  throw new Error('THREESync has been disposed');
973
992
  const now = performance.now(), controls = this._options.controls, sourceCamera = this._options.camera;
974
- const dt = this._lastUpdate === undefined ? 0 : Math.min(.1, Math.max(0, (now - this._lastUpdate) / 1000));
993
+ const elapsed = this._lastUpdate === undefined ? 0 : Math.max(0, (now - this._lastUpdate) / 1000);
994
+ const dt = Math.min(.1, elapsed);
975
995
  this._lastUpdate = now;
976
996
  let moving = false, settled = false;
977
997
  if (controls && this._options.updateControls !== false) {
978
- const position = sourceCamera?.position.clone(), zoom = sourceCamera?.zoom;
979
- moving = !!controls.update(dt);
980
- if (moving && !this._started && !controls.autoRotate && controls.enableDamping && position && sourceCamera && controls.target) {
981
- const step = position.distanceTo(sourceCamera.position);
982
- const zoomStep = Math.abs(Math.log(sourceCamera.zoom / zoom));
983
- if (step < 5e-4 * sourceCamera.position.distanceTo(controls.target) && zoomStep < 5e-4) {
984
- controls.enableDamping = false;
985
- try {
986
- controls.update(0);
987
- }
988
- finally {
989
- controls.enableDamping = true;
998
+ const orbitInputs = orbitControlsState(sourceCamera, controls);
999
+ const idle = !this._started && !controls.autoRotate && orbitInputs && this._idleControlsState && snapshotsEqual(orbitInputs, this._idleControlsState);
1000
+ if (!idle) {
1001
+ this._idleControlsState = undefined;
1002
+ const position = sourceCamera?.position.clone(), rotation = sourceCamera?.quaternion.clone(), zoom = sourceCamera?.zoom;
1003
+ const target = controls.target?.clone(), damping = controls.dampingFactor;
1004
+ const frameDamping = controls.enableDamping && damping !== undefined && damping >= 0 && damping <= 1
1005
+ ? 1 - Math.pow(1 - damping, elapsed * 60) : undefined;
1006
+ if (frameDamping !== undefined)
1007
+ controls.dampingFactor = frameDamping;
1008
+ try {
1009
+ moving = !!controls.update(dt);
1010
+ }
1011
+ finally {
1012
+ if (frameDamping !== undefined)
1013
+ controls.dampingFactor = damping;
1014
+ }
1015
+ if (!this._started && !controls.autoRotate && controls.enableDamping && position && sourceCamera && controls.target) {
1016
+ const referenceScale = frameDamping ? damping / frameDamping : 1;
1017
+ const step = position.distanceTo(sourceCamera.position) * referenceScale;
1018
+ const zoomStep = Math.abs(Math.log(sourceCamera.zoom / zoom)) * referenceScale;
1019
+ const poseChanged = step > 0 || sourceCamera.zoom !== zoom || !!(orbitInputs && (!rotation.equals(sourceCamera.quaternion) || !target.equals(controls.target)));
1020
+ // End the remaining inertia once its 60 Hz movement is imperceptible.
1021
+ // update() can return false before that tail has actually stopped.
1022
+ if ((moving || poseChanged) && step < 5e-4 * sourceCamera.position.distanceTo(controls.target) && zoomStep < 5e-4) {
1023
+ controls.enableDamping = false;
1024
+ try {
1025
+ controls.update(0);
1026
+ }
1027
+ finally {
1028
+ controls.enableDamping = true;
1029
+ }
1030
+ moving = false;
1031
+ settled = true;
990
1032
  }
991
- moving = false;
992
- settled = true;
1033
+ // Once stopped, no more spherical/cartesian round trips occur until
1034
+ // a gesture, source pose, parent transform or public control option changes.
1035
+ if (orbitInputs && (settled || (!moving && !poseChanged)))
1036
+ this._idleControlsState = orbitControlsState(sourceCamera, controls);
1037
+ }
1038
+ else if (orbitInputs && !this._started && !controls.autoRotate && !moving) {
1039
+ this._idleControlsState = orbitControlsState(sourceCamera, controls);
993
1040
  }
994
1041
  }
995
1042
  }
@@ -1025,6 +1072,7 @@ export class THREESync extends Sync {
1025
1072
  this._options.controls?.removeEventListener('start', this._start);
1026
1073
  this._options.controls?.removeEventListener('end', this._end);
1027
1074
  this._store = createConversionStore();
1075
+ this._idleControlsState = undefined;
1028
1076
  this._disposed = true;
1029
1077
  super.dispose();
1030
1078
  }