Skip to content

Commit

Permalink
GlobeControls: Fix zoom speed, improve zoom flexibility (#598)
Browse files Browse the repository at this point in the history
* Add progress

* Improve transition point
  • Loading branch information
gkjohnson committed Jul 3, 2024
1 parent d3700b2 commit 20d37ca
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 47 deletions.
26 changes: 18 additions & 8 deletions src/three/controls/EnvironmentControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
Raycaster,
Plane,
EventDispatcher,
MathUtils,
} from 'three';
import { PivotPointMesh } from './PivotPointMesh.js';
import { PointerTracker } from './PointerTracker.js';
Expand Down Expand Up @@ -84,7 +85,7 @@ export class EnvironmentControls extends EventDispatcher {
this.zoomSpeed = 1;

this.reorientOnDrag = true;
this.reorientOnZoom = false;
this.scaleZoomOrientationAtEdges = false;
this.adjustHeight = true;

// internal state
Expand Down Expand Up @@ -895,7 +896,7 @@ export class EnvironmentControls extends EventDispatcher {
zoomDirectionSet,
zoomPointSet,
reorientOnDrag,
reorientOnZoom
scaleZoomOrientationAtEdges,
} = this;

camera.updateMatrixWorld();
Expand All @@ -907,17 +908,26 @@ export class EnvironmentControls extends EventDispatcher {
const action = state;
if ( zoomDirectionSet && ( zoomPointSet || this._updateZoomPoint() ) ) {

if ( reorientOnZoom ) {
const v = new Vector3();
this.getUpDirection( zoomPoint, v );

// rotates the camera position around the point being zoomed in to
makeRotateAroundPoint( zoomPoint, _quaternion, _rotMatrix );
camera.matrixWorld.premultiply( _rotMatrix );
camera.matrixWorld.decompose( camera.position, camera.quaternion, _vec );
if ( ! scaleZoomOrientationAtEdges ) {

zoomDirection.subVectors( zoomPoint, camera.position ).normalize();
const quat = new Quaternion();
let amt = Math.max( v.dot( up ) - 0.6, 0 ) / 0.4;
amt = MathUtils.mapLinear( amt, 0, 0.5, 0, 1 );
amt = Math.min( amt, 1 );
_quaternion.slerp( quat, 1.0 - amt );

}

// rotates the camera position around the point being zoomed in to
makeRotateAroundPoint( zoomPoint, _quaternion, _rotMatrix );
camera.matrixWorld.premultiply( _rotMatrix );
camera.matrixWorld.decompose( camera.position, camera.quaternion, _vec );

zoomDirection.subVectors( zoomPoint, camera.position ).normalize();

} else if ( action === DRAG && reorientOnDrag ) {

// If we're dragging then reorient around the drag point
Expand Down
80 changes: 41 additions & 39 deletions src/three/controls/GlobeControls.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ const _prevPointer = new Vector2();
const _deltaPointer = new Vector2();

const MIN_ELEVATION = 10;
const MAX_GLOBE_DISTANCE = 2 * 1e7;
const GLOBE_TRANSITION_THRESHOLD = 0.75 * 1e7;
const MAX_GLOBE_DISTANCE = 5 * 1e7;
const GLOBE_TRANSITION_THRESHOLD = 3 * 1e7;

Check warning on line 30 in src/three/controls/GlobeControls.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'GLOBE_TRANSITION_THRESHOLD' is assigned a value but never used

Check warning on line 30 in src/three/controls/GlobeControls.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'GLOBE_TRANSITION_THRESHOLD' is assigned a value but never used
export class GlobeControls extends EnvironmentControls {

get ellipsoid() {
Expand Down Expand Up @@ -119,35 +119,18 @@ export class GlobeControls extends EnvironmentControls {

}

super.update();

const {
camera,
tilesGroup,
pivotMesh,
} = this;

// clamp the camera distance
let distanceToCenter = this.getDistanceToCenter();
const maxDistance = this._getMaxCameraDistance();
if ( distanceToCenter > maxDistance ) {

_vec.setFromMatrixPosition( tilesGroup.matrixWorld ).sub( camera.position ).normalize().multiplyScalar( - 1 );
camera.position.setFromMatrixPosition( tilesGroup.matrixWorld ).addScaledVector( _vec, maxDistance );
camera.updateMatrixWorld();

distanceToCenter = maxDistance;

}

// TODO: handle ortho

// if we're outside the transition threshold then we toggle some reorientation behavior
// when adjusting the up frame while moving the camera
if ( this._isNearControls() ) {

this.reorientOnDrag = true;
this.reorientOnZoom = false;
this.scaleZoomOrientationAtEdges = this.zoomDelta > 0;

} else {

Expand All @@ -157,7 +140,23 @@ export class GlobeControls extends EnvironmentControls {

}
this.reorientOnDrag = false;
this.reorientOnZoom = true;
this.scaleZoomOrientationAtEdges = true;

}

// fire basic controls update
super.update();

// clamp the camera distance
let distanceToCenter = this.getDistanceToCenter();
const maxDistance = this._getMaxCameraDistance();
if ( distanceToCenter > maxDistance ) {

_vec.setFromMatrixPosition( tilesGroup.matrixWorld ).sub( camera.position ).normalize().multiplyScalar( - 1 );
camera.position.setFromMatrixPosition( tilesGroup.matrixWorld ).addScaledVector( _vec, maxDistance );
camera.updateMatrixWorld();

distanceToCenter = maxDistance;

}

Expand Down Expand Up @@ -300,7 +299,7 @@ export class GlobeControls extends EnvironmentControls {

_updateZoom() {

const zoomDelta = this.zoomDelta;
const { zoomDelta, ellipsoid, zoomSpeed } = this;
if ( this._isNearControls() || zoomDelta > 0 ) {

super._updateZoom();
Expand All @@ -314,9 +313,14 @@ export class GlobeControls extends EnvironmentControls {
this._tiltTowardsCenter( MathUtils.lerp( 0, 0.2, alpha ) );
this._alignCameraUpToNorth( MathUtils.lerp( 0, 0.1, alpha ) );

// calculate zoom in a similar way to environment controls so
// the zoom speeds are comparable
const dist = this.getDistanceToCenter() - ellipsoid.radius.x;
const scale = zoomDelta * dist * zoomSpeed * 0.0025;

// zoom out directly from the globe center
this.getVectorToCenter( _vec );
this.camera.position.addScaledVector( _vec, zoomDelta * 0.0025 );
this.getVectorToCenter( _vec ).normalize();
this.camera.position.addScaledVector( _vec, scale );
this.camera.updateMatrixWorld();

this.zoomDelta = 0;
Expand Down Expand Up @@ -384,24 +388,21 @@ export class GlobeControls extends EnvironmentControls {

_getPerspectiveTransitionDistance() {

return GLOBE_TRANSITION_THRESHOLD;

// TODO: the zooming seems to fail if the camera is too far out and the target
// up changes too much on move? Ie zooming into the horizon from afar
// const { camera, ellipsoid } = this;
// if ( ! camera.isPerspectiveCamera ) {
const { camera, ellipsoid } = this;
if ( ! camera.isPerspectiveCamera ) {

// throw new Error();
throw new Error();

// }
}

// const ellipsoidSize = Math.max( ...ellipsoid.radius ) * 2;
// const fovHoriz = 2 * Math.atan( Math.tan( MathUtils.DEG2RAD * camera.fov * 0.5 ) * camera.aspect );
// const distVert = ellipsoidSize / Math.tan( MathUtils.DEG2RAD * camera.fov * 0.5 );
// const distHoriz = ellipsoidSize / Math.tan( fovHoriz * 0.5 );
// const dist = Math.max( distVert, distHoriz );
// When the smallest fov spans 65% of the ellipsoid then we use the near controls
const ellipsoidSize = 0.65 * 2 * Math.max( ...ellipsoid.radius );
const fovHoriz = 2 * Math.atan( Math.tan( MathUtils.DEG2RAD * camera.fov * 0.5 ) * camera.aspect );
const distVert = ellipsoidSize / Math.tan( MathUtils.DEG2RAD * camera.fov * 0.5 );
const distHoriz = ellipsoidSize / Math.tan( fovHoriz * 0.5 );
const dist = Math.min( distVert, distHoriz );

// return dist * 0.7;
return dist;

}

Expand All @@ -414,7 +415,8 @@ export class GlobeControls extends EnvironmentControls {

}

const ellipsoidSize = Math.max( ...ellipsoid.radius ) * 2;
// allow for zooming out such that the ellipsoid is half the size of the largest fov
const ellipsoidSize = 2 * 2 * Math.max( ...ellipsoid.radius );
const fovHoriz = 2 * Math.atan( Math.tan( MathUtils.DEG2RAD * camera.fov * 0.5 ) * camera.aspect );
const distVert = ellipsoidSize / Math.tan( MathUtils.DEG2RAD * camera.fov * 0.5 );
const distHoriz = ellipsoidSize / Math.tan( fovHoriz * 0.5 );
Expand Down

0 comments on commit 20d37ca

Please sign in to comment.