ソースを参照

Merge pull request #144 from NASA-AMMOS/fly-controls

Add fly controls to demo
Garrett Johnson 4 年 前
コミット
47eb416707
2 ファイル変更288 行追加5 行削除
  1. 275 0
      example/FlyOrbitControls.js
  2. 13 5
      example/index.js

+ 275 - 0
example/FlyOrbitControls.js

@@ -0,0 +1,275 @@
+import { Clock, Vector3, Vector4 } from 'three';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+
+const changeEvent = { type: 'fly-change' };
+const startEvent = { type: 'fly-start' };
+const endEvent = { type: 'fly-end' };
+const tempVector = new Vector4( 0, 0, 0, 0 );
+export class FlyOrbitControls extends OrbitControls {
+
+	constructor( camera, domElement ) {
+
+		// Disable use of shift key so we can use it for acceleration
+		const disableShiftKeyCallback = e => {
+
+			if ( this.enabled ) {
+
+				Object.defineProperty( e, 'shiftKey', { get() { return false } } );
+
+			}
+
+		};
+
+		domElement.addEventListener( 'pointerdown', disableShiftKeyCallback );
+
+		super( camera, domElement );
+
+		this.enableKeys = false;
+		this.enableFlight = true;
+		this.baseSpeed = 1;
+		this.fastSpeed = 4;
+		this.forwardKey = 'w';
+		this.backKey = 's';
+		this.leftKey = 'a';
+		this.rightKey = 'd';
+		this.upKey = 'q';
+		this.downKey = 'e';
+		this.fastKey = 'shift';
+
+		let fastHeld = false;
+		let forwardHeld = false;
+		let backHeld = false;
+		let leftHeld = false;
+		let rightHeld = false;
+		let upHeld = false;
+		let downHeld = false;
+
+		let originalDistance = 0
+		let originalMinDistance = 0;
+		let originalMaxDistance = 0;
+		let rafHandle = - 1;
+		const originalTarget = new Vector3();
+		const clock = new Clock();
+
+		const endFlight = () => {
+
+			if ( rafHandle !== - 1 ) {
+
+				// cancel the animation playing
+				cancelAnimationFrame( rafHandle );
+				rafHandle = - 1;
+
+				// store the original distances for the controls
+				this.minDistance = originalMinDistance;
+				this.maxDistance = originalMaxDistance;
+
+				const targetDistance = Math.min( originalDistance, camera.position.distanceTo( originalTarget ) );
+				tempVector
+					.set( 0, 0, - 1, 0 )
+					.applyMatrix4( camera.matrixWorld );
+				this
+					.target
+					.copy( camera.position )
+					.addScaledVector( tempVector, targetDistance );
+
+				this.dispatchEvent( endEvent );
+
+			}
+
+		};
+
+		const updateFlight = () => {
+
+			if ( ! this.enabled || ! this.enableFlight ) {
+
+				return;
+
+			}
+
+			rafHandle = requestAnimationFrame( updateFlight );
+
+			// get the direction
+			tempVector.set( 0, 0, 0, 0 );
+			if ( forwardHeld ) tempVector.z -= 1;
+			if ( backHeld ) tempVector.z += 1;
+			if ( leftHeld ) tempVector.x -= 1;
+			if ( rightHeld ) tempVector.x += 1;
+			if ( upHeld ) tempVector.y += 1;
+			if ( downHeld ) tempVector.y -= 1;
+			tempVector.applyMatrix4( camera.matrixWorld );
+
+			// apply the movement
+			const delta = 60 * clock.getDelta();
+			const speed = fastHeld ? this.fastSpeed : this.baseSpeed;
+			camera
+				.position
+				.addScaledVector( tempVector, speed * delta );
+			this
+				.target
+				.addScaledVector( tempVector, speed * delta );
+
+			this.dispatchEvent( changeEvent );
+
+		};
+
+		const keyDownCallback = e => {
+
+			const key = e.key.toLowerCase();
+
+			if ( rafHandle === - 1 ) {
+
+				originalMaxDistance = this.maxDistance;
+				originalMinDistance = this.minDistance;
+				originalDistance = camera.position.distanceTo( this.target );
+				originalTarget.copy( this.target );
+
+			}
+
+			switch ( key ) {
+
+				case this.forwardKey:
+					forwardHeld = true;
+					break;
+				case this.backKey:
+					backHeld = true;
+					break;
+				case this.leftKey:
+					leftHeld = true;
+					break;
+				case this.rightKey:
+					rightHeld = true;
+					break;
+				case this.upKey:
+					upHeld = true;
+					break;
+				case this.downKey:
+					downHeld = true;
+					break;
+				case this.fastKey:
+					fastHeld = true;
+					break;
+
+			}
+
+			switch ( key ) {
+
+				case this.fastKey:
+				case this.forwardKey:
+				case this.backKey:
+				case this.leftKey:
+				case this.rightKey:
+				case this.upKey:
+				case this.downKey:
+					e.stopPropagation();
+					e.preventDefault();
+
+			}
+
+			if ( forwardHeld || backHeld || leftHeld || rightHeld || upHeld || downHeld ) {
+
+				this.minDistance = 0.01;
+				this.maxDistance = 0.01;
+
+				// Move the orbit target out to just in front of the camera
+				tempVector
+					.set( 0, 0, - 1, 0 )
+					.applyMatrix4( camera.matrixWorld );
+				this
+					.target
+					.copy( camera.position )
+					.addScaledVector( tempVector, 0.01 );
+
+				if ( rafHandle === - 1 ) {
+
+					// start the flight and reset the clock
+					this.dispatchEvent( startEvent );
+					clock.getDelta();
+					updateFlight();
+
+				}
+
+			}
+
+		};
+
+		const keyUpCallback = e => {
+
+			const key = e.key.toLowerCase();
+
+			switch ( key ) {
+
+				case this.fastKey:
+				case this.forwardKey:
+				case this.backKey:
+				case this.leftKey:
+				case this.rightKey:
+				case this.upKey:
+				case this.downKey:
+					e.stopPropagation();
+					e.preventDefault();
+
+			}
+
+			switch( key ) {
+
+				case this.forwardKey:
+					forwardHeld = false;
+					break;
+				case this.backKey:
+					backHeld = false;
+					break;
+				case this.leftKey:
+					leftHeld = false;
+					break;
+				case this.rightKey:
+					rightHeld = false;
+					break;
+				case this.upKey:
+					upHeld = false;
+					break;
+				case this.downKey:
+					downHeld = false;
+					break;
+				case this.fastKey:
+					fastHeld = false;
+					break;
+
+			}
+
+			if ( ! ( forwardHeld || backHeld || leftHeld || rightHeld || upHeld || downHeld ) ) {
+
+				endFlight();
+
+			}
+
+		};
+
+		const blurCallback = () => {
+
+			endFlight();
+
+		};
+
+		this.blurCallback = blurCallback;
+		this.keyDownCallback = keyDownCallback;
+		this.keyUpCallback = keyUpCallback;
+		this.disableShiftKeyCallback = disableShiftKeyCallback;
+
+		this.domElement.addEventListener( 'blur', blurCallback );
+		this.domElement.addEventListener( 'keydown', keyDownCallback );
+		this.domElement.addEventListener( 'keyup', keyUpCallback );
+
+	}
+
+	dispose() {
+
+		super.dispose();
+
+		this.domElement.removeEventListener( 'blur', this.blurCallback );
+		this.domElement.removeEventListener( 'keydown', this.keyDownCallback );
+		this.domElement.removeEventListener( 'keyup', this.keyUpCallback );
+		this.domElement.removeEventListener( 'pointerdown', this.disableShiftKeyCallback );
+
+	}
+
+}

+ 13 - 5
example/index.js

@@ -25,9 +25,11 @@ import {
 	Group,
 	TorusBufferGeometry,
 	OrthographicCamera,
-	sRGBEncoding
+	sRGBEncoding,
+	Vector4,
+	Vector3,
 } from 'three';
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { FlyOrbitControls } from './FlyOrbitControls.js';
 import { BufferGeometryUtils } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
 import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
 import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
@@ -46,6 +48,9 @@ let box;
 let raycaster, mouse, rayIntersect, lastHoveredElement;
 let offsetParent;
 let statsContainer, stats;
+const moveDirection = new Vector4( 0, 0, 0, 0 );
+const originalTarget = new Vector3( 0, 0, 0 );
+let originalDistance = 0;
 
 let params = {
 
@@ -120,6 +125,7 @@ function init() {
 	renderer.outputEncoding = sRGBEncoding;
 
 	document.body.appendChild( renderer.domElement );
+	renderer.domElement.tabIndex = 1;
 
 	camera = new PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 4000 );
 	camera.position.set( 400, 400, 400 );
@@ -146,8 +152,9 @@ function init() {
 	secondRenderer.domElement.style.right = '0';
 	secondRenderer.domElement.style.top = '0';
 	secondRenderer.domElement.style.outline = '#0f1416 solid 2px';
+	secondRenderer.domElement.tabIndex = 1;
 
-	secondControls = new OrbitControls( secondCamera, secondRenderer.domElement );
+	secondControls = new FlyOrbitControls( secondCamera, secondRenderer.domElement );
 	secondControls.screenSpacePanning = false;
 	secondControls.minDistance = 1;
 	secondControls.maxDistance = 2000;
@@ -170,14 +177,15 @@ function init() {
 	thirdPersonRenderer.domElement.style.position = 'fixed';
 	thirdPersonRenderer.domElement.style.left = '5px';
 	thirdPersonRenderer.domElement.style.bottom = '5px';
+	thirdPersonRenderer.domElement.tabIndex = 1;
 
-	thirdPersonControls = new OrbitControls( thirdPersonCamera, thirdPersonRenderer.domElement );
+	thirdPersonControls = new FlyOrbitControls( thirdPersonCamera, thirdPersonRenderer.domElement );
 	thirdPersonControls.screenSpacePanning = false;
 	thirdPersonControls.minDistance = 1;
 	thirdPersonControls.maxDistance = 2000;
 
 	// controls
-	controls = new OrbitControls( camera, renderer.domElement );
+	controls = new FlyOrbitControls( camera, renderer.domElement );
 	controls.screenSpacePanning = false;
 	controls.minDistance = 1;
 	controls.maxDistance = 2000;