UNPKG

three.fly

Version:

Three.js fly controls as common js module

277 lines (224 loc) 7.15 kB
/** * @author James Baicoianu / http://www.baicoianu.com/ * Source: https://github.com/mrdoob/three.js/blob/master/examples/js/controls/FlyControls.js * * Adopted to common js by Andrei Kashcha */ var eventify = require('ngraph.events'); var createKeyMap = require('./keymap.js'); module.exports = fly; function fly(camera, domElement, THREE) { domElement = domElement || document; domElement.setAttribute('tabindex', -1); var moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 }; var api = { rollSpeed: 0.005, movementSpeed: 1, dragToLook: true, autoForward: false, /** * Requests to update camera position according to the currently pressed * keys/mouse */ update: update, /** * Returns true if we are moving camera at the moment */ isMoving: isMoving, /** * Releases all event handlers */ destroy: destroy, /** * This allows external developers to better control our internal state * Super flexible, yet a bit dangerous */ moveState: moveState, updateMovementVector: updateMovementVector, updateRotationVector: updateRotationVector, /** * Toggles dragToLook setting. When dragToLook is set to false, then * camera always attempts to focus on current mouse position. The only * stable point in the visualization is middle of the screen. */ toggleDragToLook: toggleDragToLook }; eventify(api); var tmpQuaternion = new THREE.Quaternion(); var isMouseDown = 0; var keyMap = createKeyMap(); // we will remember what keys should be releaed in global keyup handler: var pendingKeyUp = Object.create(null); var moveVector = new THREE.Vector3(0, 0, 0); var rotationVector = new THREE.Vector3(0, 0, 0); var moveArgs = { move: moveVector, rotate: rotationVector }; // these are local to the scene container. We want to initiate actions only // when we have focus domElement.addEventListener('mousedown', mousedown, false); domElement.addEventListener('keydown', keydown, false); // These are global since we can loose control otherwise and miss keyup/move // events. document.addEventListener('mousemove', mousemove, false); document.addEventListener('keyup', keyup, false); updateMovementVector(); updateRotationVector(); return api; function isMoving() { return moveState.up || moveState.down || moveState.left || moveState.right || moveState.forward || moveState.back || moveState.pitchUp || moveState.pitchDown || moveState.yawLeft || moveState.yawRight || moveState.rollLeft || moveState.rollRight; } function toggleDragToLook() { api.dragToLook = !api.dragToLook; api.moveState.yawLeft = 0; api.moveState.pitchDown = 0; updateRotationVector(); return api.dragToLook; } function update(delta) { var moveMult = delta * api.movementSpeed; var rotMult = delta * api.rollSpeed; camera.translateX(moveVector.x * moveMult); camera.translateY(moveVector.y * moveMult); camera.translateZ(moveVector.z * moveMult); tmpQuaternion.set(rotationVector.x * rotMult, rotationVector.y * rotMult, rotationVector.z * rotMult, 1).normalize(); camera.quaternion.multiply(tmpQuaternion); // expose the rotation vector for convenience camera.rotation.setFromQuaternion(camera.quaternion, camera.rotation.order); } function keydown(event) { if (isModifierKey(event)) return; var motion = keyMap[event.keyCode]; if (motion) { moveState[motion.name] = 1; // we need to make sure that global key up event clears this motion: pendingKeyUp[event.keyCode] = true; updateMovementVector(); updateRotationVector(); api.fire('move', moveArgs); } } function isModifierKey(e) { return e.altKey || e.ctrlKey || e.metaKey; } function keyup(event) { if (!pendingKeyUp[event.keyCode]) return; pendingKeyUp[event.keyCode] = false; var motion = keyMap[event.keyCode]; moveState[motion.name] = 0; updateMovementVector(); updateRotationVector(); api.fire('move', moveArgs); } function mousedown(event) { if (domElement !== document) { domElement.focus(); } document.addEventListener('mouseup', mouseup, false); event.preventDefault(); //event.stopPropagation(); if (api.dragToLook) { isMouseDown = true; } else { switch (event.button) { case 0: moveState.forward = 1; break; case 2: moveState.back = 1; break; } updateMovementVector(); } api.fire('move', moveArgs); } function mousemove(event) { if (!api.dragToLook || isMouseDown) { var container = getContainerDimensions(); var halfWidth = container.size[0] / 2; var halfHeight = container.size[1] / 2; moveState.yawLeft = -((event.pageX - container.offset[0]) - halfWidth) / halfWidth; moveState.pitchDown = ((event.pageY - container.offset[1]) - halfHeight) / halfHeight; updateRotationVector(); api.fire('move', moveArgs); } } function mouseup(event) { event.preventDefault(); if (isMouseDown) { document.removeEventListener('mouseup', mouseup); isMouseDown = false; } if (api.dragToLook) { moveState.yawLeft = moveState.pitchDown = 0; } else { switch (event.button) { case 0: moveState.forward = 0; break; case 2: moveState.back = 0; break; } updateMovementVector(); } updateRotationVector(); api.fire('move', moveArgs); } function updateMovementVector() { var forward = (moveState.forward || (api.autoForward && !moveState.back)) ? 1 : 0; moveVector.x = (-moveState.left + moveState.right); moveVector.y = (-moveState.down + moveState.up); moveVector.z = (-forward + moveState.back); } function updateRotationVector() { rotationVector.x = (-moveState.pitchDown + moveState.pitchUp); rotationVector.y = (-moveState.yawRight + moveState.yawLeft); rotationVector.z = (-moveState.rollRight + moveState.rollLeft); } function getContainerDimensions() { if (domElement !== document) { return { size: [domElement.offsetWidth, domElement.offsetHeight], offset: [domElement.offsetLeft, domElement.offsetTop] }; } else { return { size: [window.innerWidth, window.innerHeight], offset: [0, 0] }; } } function destroy() { document.removeEventListener('mouseup', mouseup); document.removeEventListener('mousemove', mousemove, false); document.removeEventListener('keyup', keyup, false); domElement.removeEventListener('mousedown', mousedown, false); domElement.removeEventListener('keydown', keydown, false); } }