Skip to content

Commit 7be9f2e

Browse files
committed
Updates
1 parent 4c685d7 commit 7be9f2e

4 files changed

Lines changed: 149 additions & 15 deletions

File tree

example/three/googleMapsExample.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ function init() {
135135
// controls
136136
controls = new GlobeControls( scene, transition.camera, renderer.domElement, null );
137137
controls.enableDamping = true;
138+
controls.enableFlight = true;
139+
controls.flightSpeed = 0.5;
140+
controls.maxAltitude = Math.PI / 2;
138141

139142
// initialize tiles
140143
reinstantiateTiles();

src/three/renderer/API.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,36 @@ useFallbackPlane: boolean
600600
When true, the fallback plane is used when raycasting misses scene geometry. Default is true.
601601

602602

603+
### .enableFlight
604+
605+
```js
606+
enableFlight: boolean
607+
```
608+
609+
When true, enables keyboard flight: W/A/S/D and arrow keys move forward/back/strafe, Q/E move
610+
up/down, and Shift multiplies speed by `flightSpeedMultiplier`. Right-click or Shift+left-click
611+
enters free-look mode, rotating the camera in place without requiring a surface hit. Only
612+
supported for perspective cameras. Default is false.
613+
614+
615+
### .flightSpeed
616+
617+
```js
618+
flightSpeed: number
619+
```
620+
621+
Base camera speed in world units per second during keyboard flight. Default is 10.
622+
623+
624+
### .flightSpeedMultiplier
625+
626+
```js
627+
flightSpeedMultiplier: number
628+
```
629+
630+
Speed multiplier applied when the fast key is held during flight. Default is 4.
631+
632+
603633
### .constructor
604634

605635
```js

src/three/renderer/controls/EnvironmentControls.js

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -192,13 +192,12 @@ export class EnvironmentControls extends EventDispatcher {
192192
*/
193193
this.useFallbackPlane = true;
194194

195-
// settings for GlobeControls
196-
this.scaleZoomOrientationAtEdges = false;
197-
this.autoAdjustCameraRotation = true;
198-
199195
// flight
200196
/**
201-
* When true, WASD/QE keys move the camera freely through space. Default is false.
197+
* When true, enables keyboard flight: W/A/S/D and arrow keys move forward/back/strafe, Q/E move
198+
* up/down, and Shift multiplies speed by `flightSpeedMultiplier`. Right-click or Shift+left-click
199+
* enters free-look mode, rotating the camera in place without requiring a surface hit. Only
200+
* supported for perspective cameras. Default is false.
202201
* @type {boolean}
203202
*/
204203
this.enableFlight = false;
@@ -215,7 +214,10 @@ export class EnvironmentControls extends EventDispatcher {
215214
*/
216215
this.flightSpeedMultiplier = 4;
217216

218-
this._keysDown = new Set();
217+
218+
// settings for GlobeControls
219+
this.scaleZoomOrientationAtEdges = false;
220+
this.autoAdjustCameraRotation = true;
219221

220222
// internal state
221223
this.state = NONE;
@@ -252,6 +254,8 @@ export class EnvironmentControls extends EventDispatcher {
252254
this.up = new Vector3( 0, 1, 0 );
253255
this._lastTime = performance.now();
254256

257+
this._keysDown = new Set();
258+
255259
this._detachCallback = null;
256260
this._upInitialized = false;
257261
this._lastUsedState = NONE;
@@ -370,6 +374,8 @@ export class EnvironmentControls extends EventDispatcher {
370374
scene,
371375
pivotPoint,
372376
enabled,
377+
enableFlight,
378+
_keysDown,
373379
} = this;
374380

375381
// init the pointer
@@ -411,12 +417,25 @@ export class EnvironmentControls extends EventDispatcher {
411417

412418
}
413419

414-
// flight mode with shift held: free-look around the camera origin, skip raycasting
420+
// free-look around the camera origin when flight is active with any flight key held, or shift/right-click
421+
const anyFlightKey =
422+
_keysDown.has( 'w' ) ||
423+
_keysDown.has( 's' ) ||
424+
_keysDown.has( 'a' ) ||
425+
_keysDown.has( 'd' ) ||
426+
_keysDown.has( 'q' ) ||
427+
_keysDown.has( 'e' ) ||
428+
_keysDown.has( 'arrowup' ) ||
429+
_keysDown.has( 'arrowdown' ) ||
430+
_keysDown.has( 'arrowleft' ) ||
431+
_keysDown.has( 'arrowright' ) ||
432+
_keysDown.has( 'shift' );
433+
415434
if (
416-
this.enableFlight &&
435+
enableFlight && anyFlightKey &&
417436
! pointerTracker.isPointerTouch() && (
418437
pointerTracker.isRightClicked() ||
419-
pointerTracker.isLeftClicked() && e.shiftKey
438+
pointerTracker.isLeftClicked()
420439
)
421440
) {
422441

@@ -661,7 +680,28 @@ export class EnvironmentControls extends EventDispatcher {
661680

662681
const keydownCallback = e => {
663682

664-
this._keysDown.add( e.key.toLowerCase() );
683+
const { _keysDown, state } = this;
684+
685+
_keysDown.add( e.key.toLowerCase() );
686+
687+
const anyFlightKey =
688+
_keysDown.has( 'w' ) ||
689+
_keysDown.has( 's' ) ||
690+
_keysDown.has( 'a' ) ||
691+
_keysDown.has( 'd' ) ||
692+
_keysDown.has( 'q' ) ||
693+
_keysDown.has( 'e' ) ||
694+
_keysDown.has( 'arrowup' ) ||
695+
_keysDown.has( 'arrowdown' ) ||
696+
_keysDown.has( 'arrowleft' ) ||
697+
_keysDown.has( 'arrowright' );
698+
699+
if ( anyFlightKey && state !== FREE_ROTATE ) {
700+
701+
this.resetState();
702+
703+
}
704+
665705

666706
};
667707

@@ -1129,6 +1169,12 @@ export class EnvironmentControls extends EventDispatcher {
11291169

11301170
}
11311171

1172+
_getFlightSpeedScale() {
1173+
1174+
return 1;
1175+
1176+
}
1177+
11321178
_updateFlight( deltaTime ) {
11331179

11341180
const {
@@ -1139,7 +1185,7 @@ export class EnvironmentControls extends EventDispatcher {
11391185
_keysDown,
11401186
} = this;
11411187

1142-
if ( ! enableFlight ) {
1188+
if ( ! enableFlight || camera.isOrthographicCamera ) {
11431189

11441190
return false;
11451191

@@ -1155,7 +1201,7 @@ export class EnvironmentControls extends EventDispatcher {
11551201

11561202
// calculate speed
11571203
const mult = _keysDown.has( 'shift' ) ? flightSpeedMultiplier : 1;
1158-
const speed = mult * flightSpeed * deltaTime;
1204+
const speed = mult * flightSpeed * this._getFlightSpeedScale() * deltaTime;
11591205

11601206
// calculate direction
11611207
_flightDir.set(

src/three/renderer/controls/GlobeControls.js

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
Ray,
99
Group,
1010
} from 'three';
11-
import { DRAG, ZOOM, EnvironmentControls, NONE } from './EnvironmentControls.js';
11+
import { DRAG, ZOOM, FREE_ROTATE, EnvironmentControls, NONE } from './EnvironmentControls.js';
1212
import { makeRotateAroundPoint, adjustedPointerToCoords, setRaycasterFromCamera } from './utils.js';
1313
import { Ellipsoid } from '../math/Ellipsoid.js';
1414
import { WGS84_ELLIPSOID } from '../math/GeoConstants.js';
@@ -279,7 +279,7 @@ export class GlobeControls extends EnvironmentControls {
279279
this.adjustCamera( camera );
280280

281281
// align the camera up vector if the camera as updated
282-
if ( adjustCameraRotation && this._isNearControls() ) {
282+
if ( adjustCameraRotation && ( this._isNearControls() || this.state === FREE_ROTATE ) ) {
283283

284284
this.getCameraUpDirection( _globalUp );
285285
this._alignCameraUp( _globalUp, 1 );
@@ -459,6 +459,54 @@ export class GlobeControls extends EnvironmentControls {
459459

460460
}
461461

462+
_getFlightSpeedScale() {
463+
464+
// Scale speed proportionally to altitude so movement feels consistent at any distance.
465+
// The 1000 m floor prevents movement becoming imperceptibly slow near the surface.
466+
const altitude = this.getDistanceToCenter() - this._getMaxWorldRadius();
467+
return 2 * Math.max( altitude, 1000 );
468+
469+
}
470+
471+
_updateFlight( deltaTime ) {
472+
473+
const { camera } = this;
474+
475+
const didFly = super._updateFlight( deltaTime );
476+
if ( didFly ) {
477+
478+
// prevent flying past the point where the globe would be too small, just like mouse zoom.
479+
const maxDistance = this._getMaxPerspectiveDistance();
480+
const distToCenter = this.getDistanceToCenter();
481+
if ( distToCenter > maxDistance ) {
482+
483+
this.getVectorToCenter( _vec ).normalize();
484+
camera.position.addScaledVector( _vec, distToCenter - maxDistance );
485+
camera.updateMatrixWorld();
486+
487+
}
488+
489+
// Outside the near-controls zone (high altitude / space view), gently nudge the
490+
// camera to keep the globe centered and the horizon level — matching the behavior
491+
// of scroll-zoom at the same distance. Alpha scales from 0 at the transition
492+
// threshold to full strength at maxDistance.
493+
if ( ! this._isNearControls() ) {
494+
495+
const distanceAlpha = MathUtils.clamp(
496+
MathUtils.mapLinear( this.getDistanceToCenter(), this._getPerspectiveTransitionDistance(), maxDistance, 0, 1 ),
497+
0, 1,
498+
);
499+
this._tiltTowardsCenter( 0.02 * distanceAlpha );
500+
this._alignCameraUpToNorth( 0.01 * distanceAlpha );
501+
502+
}
503+
504+
}
505+
506+
return didFly;
507+
508+
}
509+
462510
_updatePosition( deltaTime ) {
463511

464512
if ( this.state === DRAG ) {
@@ -540,6 +588,14 @@ export class GlobeControls extends EnvironmentControls {
540588
// disable rotation once we're outside the control transition
541589
_updateRotation( ...args ) {
542590

591+
// FREE_ROTATE is always allowed regardless of globe proximity
592+
if ( this.state === FREE_ROTATE ) {
593+
594+
super._updateRotation( ...args );
595+
return;
596+
597+
}
598+
543599
if ( this._rotationMode === 1 || this._isNearControls() ) {
544600

545601
this._rotationMode = 1;
@@ -552,7 +608,6 @@ export class GlobeControls extends EnvironmentControls {
552608

553609
}
554610

555-
556611
}
557612

558613
_updateZoom() {

0 commit comments

Comments
 (0)