Redo controls, try making them somewhat modular
This commit is contained in:
parent
a4ee07aa5c
commit
d16b71765f
|
@ -8,6 +8,7 @@
|
|||
</head>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<div id="map-container" style="position: absolute; width: 100%; height: 100%;"></div>
|
||||
<!--<div id="debug-div" style="position: fixed; top: 10px; left: 10px; background-color: #111; opacity: 0.8; color: white; padding: 10px"></div>-->
|
||||
|
||||
<script type="text/javascript" src="js/three.js"></script>
|
||||
<script type="text/javascript" src="js/hammer.js"></script>
|
||||
|
@ -19,6 +20,9 @@
|
|||
// show stats panel
|
||||
bluemap.stats.showPanel(1);
|
||||
|
||||
let mapControls = bluemap.controlsManager.controls;
|
||||
let freeControls = new BlueMap.FreeFlightControls(bluemap.rootElement);
|
||||
|
||||
// load map
|
||||
let maps = [];
|
||||
let markerManager = null;
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import {FileLoader, Object3D} from "three";
|
||||
import {Map} from "./map/Map";
|
||||
|
||||
export { MapViewer } from "./MapViewer";
|
||||
export * from "./util/Utils";
|
||||
export { MarkerFileManager } from "./markers/MarkerFileManager";
|
||||
export { PlayerMarkerManager } from "./markers/PlayerMarkerManager";
|
||||
export * from "./MapViewer";
|
||||
export * from "./markers/MarkerFileManager";
|
||||
export * from "./markers/PlayerMarkerManager";
|
||||
export * from "./controls/map/MapControls";
|
||||
export * from "./controls/freeflight/FreeFlightControls";
|
||||
|
||||
/**
|
||||
* Loads and returns a promise with an array of Maps loaded from that root-path.<br>
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import {
|
||||
PerspectiveCamera,
|
||||
WebGLRenderer,
|
||||
Vector2, Raycaster, Layers, Scene
|
||||
} from "three";
|
||||
import {Layers, PerspectiveCamera, Raycaster, Scene, Vector2, WebGLRenderer} from "three";
|
||||
import {Map} from "./map/Map";
|
||||
import {SkyboxScene} from "./skybox/SkyboxScene";
|
||||
import {ControlsManager} from "./controls/ControlsManager";
|
||||
import {MapControls} from "./controls/MapControls";
|
||||
import {MapControls} from "./controls/map/MapControls";
|
||||
import Stats from "./util/Stats";
|
||||
import {alert, dispatchEvent, elementOffset, htmlToElement} from "./util/Utils";
|
||||
import {TileManager} from "./map/TileManager";
|
||||
|
@ -16,6 +12,7 @@ import {LOWRES_VERTEX_SHADER} from "./map/lowres/LowresVertexShader";
|
|||
import {LOWRES_FRAGMENT_SHADER} from "./map/lowres/LowresFragmentShader";
|
||||
import {CombinedCamera} from "./util/CombinedCamera";
|
||||
import {CSS2DRenderer} from "./util/CSS2DRenderer";
|
||||
import {FreeFlightControls} from "./controls/freeflight/FreeFlightControls";
|
||||
|
||||
export class MapViewer {
|
||||
|
||||
|
@ -84,7 +81,7 @@ export class MapViewer {
|
|||
this.initializeHammer();
|
||||
|
||||
this.controlsManager = new ControlsManager(this, this.camera);
|
||||
this.controlsManager.controls = new MapControls(this.rootElement, this.hammer, this.events);
|
||||
this.controlsManager.controls = new MapControls(this.rootElement);
|
||||
|
||||
this.raycaster = new Raycaster();
|
||||
this.raycaster.layers.enableAll();
|
||||
|
@ -218,7 +215,7 @@ export class MapViewer {
|
|||
requestAnimationFrame(this.renderLoop);
|
||||
|
||||
// calculate delta time
|
||||
if (this.lastFrame <= 0) { this.lastFrame = now; }
|
||||
if (this.lastFrame <= 0) this.lastFrame = now;
|
||||
let delta = now - this.lastFrame;
|
||||
this.lastFrame = now;
|
||||
|
||||
|
@ -228,7 +225,6 @@ export class MapViewer {
|
|||
// update controls
|
||||
if (this.map != null) {
|
||||
this.controlsManager.update(delta, this.map);
|
||||
this.controlsManager.updateCamera();
|
||||
}
|
||||
|
||||
// render
|
||||
|
|
|
@ -6,7 +6,7 @@ export class ControlsManager {
|
|||
|
||||
/**
|
||||
* @param mapViewer {MapViewer}
|
||||
* @param camera {THREE.Camera}
|
||||
* @param camera {CombinedCamera}
|
||||
*/
|
||||
constructor(mapViewer, camera) {
|
||||
Object.defineProperty( this, 'isControlsManager', { value: true } );
|
||||
|
@ -14,19 +14,31 @@ export class ControlsManager {
|
|||
this.mapViewer = mapViewer;
|
||||
this.camera = camera;
|
||||
|
||||
this.positionValue = new Vector3(0, 0, 0);
|
||||
this.position = new Vector3(0, 0, 0);
|
||||
this.rotation = 0;
|
||||
this.angle = 0;
|
||||
this.tilt = 0;
|
||||
|
||||
this.rotationValue = 0;
|
||||
this.angleValue = 0;
|
||||
this.lastPosition = this.position.clone();
|
||||
this.lastRotation = this.rotation;
|
||||
this.lastAngle = this.angle;
|
||||
this.lastDistance = this.distance;
|
||||
this.lastOrtho = this.ortho;
|
||||
this.lastTilt = this.tilt;
|
||||
|
||||
this.distanceValue = 500;
|
||||
this.lastMapUpdatePosition = this.position.clone();
|
||||
|
||||
this.orthoValue = 0;
|
||||
this.averageDeltaTime = 16;
|
||||
|
||||
this.valueChanged = true;
|
||||
this.lastMapUpdatePosition = this.positionValue.clone();
|
||||
this._controls = null;
|
||||
|
||||
this.controlsValue = null;
|
||||
// start
|
||||
this.distance = 300;
|
||||
this.position.set(0, 0, 0);
|
||||
this.rotation = 0;
|
||||
this.angle = 0;
|
||||
this.tilt = 0;
|
||||
this.ortho = 0;
|
||||
|
||||
this.updateCamera();
|
||||
}
|
||||
|
@ -37,48 +49,60 @@ export class ControlsManager {
|
|||
*/
|
||||
update(deltaTime, map) {
|
||||
if (deltaTime > 50) deltaTime = 50; // assume min 20 UPS
|
||||
this.averageDeltaTime = this.averageDeltaTime * 0.9 + deltaTime * 0.1; // average delta-time to avoid choppy controls on lag-spikes
|
||||
|
||||
if (this.controlsValue && typeof this.controlsValue.update === "function")
|
||||
this.controlsValue.update(deltaTime, map);
|
||||
if (this._controls) this._controls.update(this.averageDeltaTime, map);
|
||||
|
||||
this.updateCamera();
|
||||
}
|
||||
|
||||
updateCamera() {
|
||||
if (this.valueChanged) {
|
||||
// prevent problems with the rotation when the angle is 0 (top-down) or distance is 0 (first-person)
|
||||
let rotatableAngle = this.angleValue;
|
||||
if (Math.abs(rotatableAngle) <= 0.0001) rotatableAngle = 0.0001;
|
||||
let rotatableDistance = this.distanceValue;
|
||||
if (Math.abs(rotatableDistance) <= 0.0001) rotatableDistance = -0.0001;
|
||||
let valueChanged = this.isValueChanged();
|
||||
|
||||
// fix distance for ortho-effect
|
||||
if (this.orthoValue > 0) {
|
||||
rotatableDistance = MathUtils.lerp(rotatableDistance, Math.max(rotatableDistance, 300), Math.pow(this.orthoValue, 8));
|
||||
if (valueChanged) {
|
||||
this.resetValueChanged();
|
||||
|
||||
// wrap rotation
|
||||
while (this.rotation >= Math.PI) this.rotation -= Math.PI * 2;
|
||||
while (this.rotation <= -Math.PI) this.rotation += Math.PI * 2;
|
||||
|
||||
// prevent problems with the rotation when the angle is 0 (top-down) or distance is 0 (first-person)
|
||||
let rotatableAngle = this.angle;
|
||||
if (Math.abs(rotatableAngle) <= 0.0001) rotatableAngle = 0.0001;
|
||||
else if (Math.abs(rotatableAngle) - Math.PI <= 0.0001) rotatableAngle = rotatableAngle - 0.0001;
|
||||
let rotatableDistance = this.distance;
|
||||
if (Math.abs(rotatableDistance) <= 0.0001) rotatableDistance = 0.0001;
|
||||
|
||||
// fix distance for orthogonal-camera
|
||||
if (this.ortho > 0) {
|
||||
rotatableDistance = MathUtils.lerp(rotatableDistance, Math.max(rotatableDistance, 300), Math.pow(this.ortho, 8));
|
||||
}
|
||||
|
||||
// calculate rotationVector
|
||||
let rotationVector = new Vector3(Math.sin(this.rotationValue), 0, -Math.cos(this.rotationValue)); // 0 is towards north
|
||||
let rotationVector = new Vector3(Math.sin(this.rotation), 0, -Math.cos(this.rotation)); // 0 is towards north
|
||||
let angleRotationAxis = new Vector3(0, 1, 0).cross(rotationVector);
|
||||
rotationVector.applyAxisAngle(angleRotationAxis, (Math.PI / 2) - rotatableAngle);
|
||||
rotationVector.multiplyScalar(rotatableDistance);
|
||||
|
||||
// position camera
|
||||
this.camera.position.copy(this.positionValue).sub(rotationVector);
|
||||
this.camera.lookAt(this.positionValue);
|
||||
|
||||
// update ortho
|
||||
this.camera.distance = this.distanceValue;
|
||||
this.camera.ortho = this.orthoValue;
|
||||
this.camera.rotation.set(0, 0, 0);
|
||||
this.camera.position.copy(this.position).sub(rotationVector);
|
||||
this.camera.lookAt(this.position);
|
||||
this.camera.rotateZ(this.tilt + rotatableAngle < 0 ? Math.PI : 0);
|
||||
|
||||
// optimize far/near planes
|
||||
if (this.orthoValue <= 0) {
|
||||
let near = MathUtils.clamp(this.distanceValue / 1000, 0.01, 1);
|
||||
let far = MathUtils.clamp(this.distanceValue * 2, Math.max(near + 1, 2000), this.distanceValue + 5000);
|
||||
if (this.ortho <= 0) {
|
||||
let near = MathUtils.clamp(rotatableDistance / 1000, 0.01, 1);
|
||||
let far = MathUtils.clamp(rotatableDistance * 2, Math.max(near + 1, 2000), rotatableDistance + 5000);
|
||||
if (far - near > 10000) near = far - 10000;
|
||||
this.camera.near = near;
|
||||
this.camera.far = far;
|
||||
} else {
|
||||
} else if (this.angle === 0) {
|
||||
this.camera.near = 1;
|
||||
this.camera.far = rotatableDistance + 300;
|
||||
} else {
|
||||
this.camera.near = 1;
|
||||
this.camera.far = 100000;
|
||||
}
|
||||
|
||||
// event
|
||||
|
@ -91,171 +115,85 @@ export class ControlsManager {
|
|||
// if the position changed, update map to show new position
|
||||
if (this.mapViewer.map) {
|
||||
let triggerDistance = 1;
|
||||
if (this.valueChanged) {
|
||||
triggerDistance = this.mapViewer.loadedHiresViewDistance * 0.8;
|
||||
if (valueChanged) {
|
||||
triggerDistance = this.mapViewer.loadedLowresViewDistance * 0.8;
|
||||
}
|
||||
if (
|
||||
Math.abs(this.lastMapUpdatePosition.x - this.positionValue.x) >= triggerDistance ||
|
||||
Math.abs(this.lastMapUpdatePosition.z - this.positionValue.z) >= triggerDistance
|
||||
Math.abs(this.lastMapUpdatePosition.x - this.position.x) >= triggerDistance ||
|
||||
Math.abs(this.lastMapUpdatePosition.z - this.position.z) >= triggerDistance
|
||||
) {
|
||||
this.lastMapUpdatePosition = this.positionValue.clone();
|
||||
this.mapViewer.loadMapArea(this.positionValue.x, this.positionValue.z);
|
||||
this.lastMapUpdatePosition = this.position.clone();
|
||||
this.mapViewer.loadMapArea(this.position.x, this.position.z);
|
||||
}
|
||||
}
|
||||
|
||||
this.valueChanged = false;
|
||||
}
|
||||
|
||||
handleValueChange() {
|
||||
this.valueChanged = true;
|
||||
isValueChanged() {
|
||||
return !(
|
||||
this.position.equals(this.lastPosition) &&
|
||||
this.rotation === this.lastRotation &&
|
||||
this.angle === this.lastAngle &&
|
||||
this.distance === this.lastDistance &&
|
||||
this.ortho === this.lastOrtho &&
|
||||
this.tilt === this.lastTilt
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get x() {
|
||||
return this.positionValue.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x {number}
|
||||
*/
|
||||
set x(x) {
|
||||
this.positionValue.x = x;
|
||||
this.handleValueChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get y() {
|
||||
return this.positionValue.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param y {number}
|
||||
*/
|
||||
set y(y) {
|
||||
this.positionValue.y = y;
|
||||
this.handleValueChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get z() {
|
||||
return this.positionValue.z;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param z {number}
|
||||
*/
|
||||
set z(z) {
|
||||
this.positionValue.z = z;
|
||||
this.handleValueChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {Vector3}
|
||||
*/
|
||||
get position() {
|
||||
return this.positionValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param position {Vector3}
|
||||
*/
|
||||
set position(position) {
|
||||
this.position.copy(position);
|
||||
this.handleValueChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get rotation() {
|
||||
return this.rotationValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param rotation {number}
|
||||
*/
|
||||
set rotation(rotation) {
|
||||
this.rotationValue = rotation;
|
||||
this.handleValueChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get angle() {
|
||||
return this.angleValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param angle {number}
|
||||
*/
|
||||
set angle(angle) {
|
||||
this.angleValue = angle;
|
||||
this.handleValueChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get distance() {
|
||||
return this.distanceValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param distance {number}
|
||||
*/
|
||||
set distance(distance) {
|
||||
this.distanceValue = distance;
|
||||
this.handleValueChange();
|
||||
resetValueChanged() {
|
||||
this.lastPosition.copy(this.position);
|
||||
this.lastRotation = this.rotation;
|
||||
this.lastAngle = this.angle;
|
||||
this.lastDistance = this.distance;
|
||||
this.lastOrtho = this.ortho;
|
||||
this.lastTilt = this.tilt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {number}
|
||||
*/
|
||||
get ortho() {
|
||||
return this.orthoValue;
|
||||
return this.camera.ortho;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ortho {number}
|
||||
*/
|
||||
set ortho(ortho) {
|
||||
this.orthoValue = ortho;
|
||||
this.handleValueChange();
|
||||
this.camera.ortho = ortho;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param controls {{
|
||||
get distance() {
|
||||
return this.camera.distance;
|
||||
}
|
||||
|
||||
set distance(distance) {
|
||||
this.camera.distance = distance;
|
||||
}
|
||||
|
||||
/** @typedef ControlsLike {{
|
||||
* start: function(controls: ControlsManager),
|
||||
* stop: function(),
|
||||
* update: function(deltaTime: number, map: Map)
|
||||
* }}
|
||||
|
||||
/**
|
||||
* @param controls {ControlsLike}
|
||||
*/
|
||||
set controls(controls) {
|
||||
if (this.controlsValue && typeof this.controlsValue.stop === "function")
|
||||
this.controlsValue.stop();
|
||||
if (this._controls && this._controls.stop)
|
||||
this._controls.stop();
|
||||
|
||||
this.controlsValue = controls;
|
||||
this._controls = controls;
|
||||
|
||||
if (this.controlsValue && typeof this.controlsValue.start === "function")
|
||||
this.controlsValue.start(this);
|
||||
if (this._controls && this._controls.start)
|
||||
this._controls.start(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {{
|
||||
* start: function(controls: ControlsManager),
|
||||
* stop: function(),
|
||||
* update: function(deltaTime: number, map: Map)
|
||||
* }}
|
||||
* @returns {ControlsLike}
|
||||
*/
|
||||
get controls() {
|
||||
return this.controlsValue;
|
||||
return this._controls;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
export class KeyCombination {
|
||||
|
||||
static CTRL = 0;
|
||||
static SHIFT = 1;
|
||||
static ALT = 2;
|
||||
|
||||
/**
|
||||
* @param code {string}
|
||||
* @param modifiers {...number}
|
||||
*/
|
||||
constructor(code, ...modifiers) {
|
||||
|
||||
this.code = code;
|
||||
this.ctrl = modifiers.includes(KeyCombination.CTRL) || this.code === "CtrlLeft" || this.code === "CtrlRight";
|
||||
this.shift = modifiers.includes(KeyCombination.SHIFT) || this.code === "ShiftLeft" || this.code === "ShiftRight";
|
||||
this.alt = modifiers.includes(KeyCombination.ALT) || this.code === "AltLeft" || this.code === "AltRight";
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
testDown(evt) {
|
||||
return this.code === evt.code &&
|
||||
this.ctrl === evt.ctrlKey &&
|
||||
this.shift === evt.shiftKey &&
|
||||
this.alt === evt.altKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
* @returns {boolean}
|
||||
*/
|
||||
testUp(evt) {
|
||||
return this.code === evt.code;
|
||||
}
|
||||
|
||||
static oneDown(evt, ...combinations) {
|
||||
for (let combination of combinations){
|
||||
if (combination.testDown(evt)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static oneUp(evt, ...combinations) {
|
||||
for (let combination of combinations){
|
||||
if (combination.testUp(evt)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,435 +0,0 @@
|
|||
import {MathUtils, MOUSE, Vector2, Vector3} from "three";
|
||||
import {alert} from "../util/Utils";
|
||||
|
||||
export class MapControls {
|
||||
|
||||
static STATES = {
|
||||
NONE: 0,
|
||||
MOVE: 1,
|
||||
ORBIT: 2
|
||||
};
|
||||
|
||||
static KEYS = {
|
||||
LEFT: ["ArrowLeft", "a", "A", 37, 65],
|
||||
UP: ["ArrowUp", "w", "W", 38, 87],
|
||||
RIGHT: ["ArrowRight", "d", "D", 39, 68],
|
||||
DOWN: ["ArrowDown", "s", "S", 40, 83],
|
||||
ZOOM_IN: ["+"],
|
||||
ZOOM_OUT: ["-"]
|
||||
};
|
||||
|
||||
static BUTTONS = {
|
||||
ORBIT: [MOUSE.RIGHT],
|
||||
MOVE: [MOUSE.LEFT]
|
||||
};
|
||||
|
||||
static VECTOR2_ZERO = new Vector2(0, 0);
|
||||
|
||||
/**
|
||||
* @param rootElement {Element}
|
||||
* @param hammerLib {Hammer.Manager}
|
||||
* @param events {EventTarget}
|
||||
*/
|
||||
constructor(rootElement, hammerLib, events = null) {
|
||||
Object.defineProperty( this, 'isMapControls', { value: true } );
|
||||
|
||||
this.rootElement = rootElement;
|
||||
this.hammer = hammerLib;
|
||||
this.events = events;
|
||||
|
||||
/** @type {ControlsManager} */
|
||||
this.controls = null;
|
||||
|
||||
this.targetPosition = new Vector3();
|
||||
this.positionTerrainHeight = false;
|
||||
|
||||
this.targetDistance = 400;
|
||||
this.minDistance = 10;
|
||||
this.maxDistance = 10000;
|
||||
|
||||
this.targetRotation = 0;
|
||||
|
||||
this.targetAngle = 0;
|
||||
this.minAngle = 0;
|
||||
this.maxAngle = Math.PI / 2;
|
||||
this.maxAngleForZoom = this.maxAngle;
|
||||
|
||||
this.state = MapControls.STATES.NONE;
|
||||
this.mouse = new Vector2();
|
||||
this.lastMouse = new Vector2();
|
||||
this.keyStates = {};
|
||||
this.touchStart = new Vector2();
|
||||
this.touchTiltStart = 0;
|
||||
this.lastTouchRotation = 0;
|
||||
this.touchZoomStart = 0;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param controls {ControlsManager}
|
||||
*/
|
||||
start(controls) {
|
||||
this.controls = controls;
|
||||
|
||||
this.targetPosition.copy(this.controls.position);
|
||||
this.positionTerrainHeight = false;
|
||||
|
||||
this.targetDistance = this.controls.distance;
|
||||
this.targetDistance = MathUtils.clamp(this.targetDistance, this.minDistance, this.maxDistance);
|
||||
|
||||
this.targetRotation = this.controls.rotation;
|
||||
|
||||
this.targetAngle = this.controls.angle;
|
||||
|
||||
this.updateZoom();
|
||||
|
||||
// add events
|
||||
this.rootElement.addEventListener("wheel", this.onWheel, {passive: true})
|
||||
this.hammer.on('zoomstart', this.onTouchZoomDown);
|
||||
this.hammer.on('zoommove', this.onTouchZoomMove);
|
||||
this.rootElement.addEventListener('mousedown', this.onMouseDown);
|
||||
window.addEventListener('mousemove', this.onMouseMove);
|
||||
window.addEventListener('mouseup', this.onMouseUp);
|
||||
window.addEventListener('keydown', this.onKeyDown);
|
||||
window.addEventListener('keyup', this.onKeyUp);
|
||||
this.hammer.on('movestart', this.onTouchDown);
|
||||
this.hammer.on('movemove', this.onTouchMove);
|
||||
this.hammer.on('moveend', this.onTouchUp);
|
||||
this.hammer.on('movecancel', this.onTouchUp);
|
||||
this.hammer.on('tiltstart', this.onTouchTiltDown);
|
||||
this.hammer.on('tiltmove', this.onTouchTiltMove);
|
||||
this.hammer.on('tiltend', this.onTouchTiltUp);
|
||||
this.hammer.on('tiltcancel', this.onTouchTiltUp);
|
||||
this.hammer.on('rotatestart', this.onTouchRotateDown);
|
||||
this.hammer.on('rotatemove', this.onTouchRotateMove);
|
||||
this.hammer.on('rotateend', this.onTouchRotateUp);
|
||||
this.hammer.on('rotatecancel', this.onTouchRotateUp);
|
||||
window.addEventListener('contextmenu', this.onContextMenu);
|
||||
}
|
||||
|
||||
stop() {
|
||||
// remove events
|
||||
this.rootElement.removeEventListener("wheel", this.onWheel)
|
||||
this.hammer.off('zoomstart', this.onTouchZoomDown);
|
||||
this.hammer.off('zoommove', this.onTouchZoomMove);
|
||||
this.rootElement.addEventListener('mousedown', this.onMouseDown);
|
||||
window.removeEventListener('mousemove', this.onMouseMove);
|
||||
window.removeEventListener('mouseup', this.onMouseUp);
|
||||
window.removeEventListener('keydown', this.onKeyDown);
|
||||
window.removeEventListener('keyup', this.onKeyUp);
|
||||
this.hammer.on('movestart', this.onTouchDown);
|
||||
this.hammer.off('movemove', this.onTouchMove);
|
||||
this.hammer.off('moveend', this.onTouchUp);
|
||||
this.hammer.off('movecancel', this.onTouchUp);
|
||||
this.hammer.off('tiltstart', this.onTouchTiltDown);
|
||||
this.hammer.off('tiltmove', this.onTouchTiltMove);
|
||||
this.hammer.off('tiltend', this.onTouchTiltUp);
|
||||
this.hammer.off('tiltcancel', this.onTouchTiltUp);
|
||||
this.hammer.off('rotatestart', this.onTouchRotateDown);
|
||||
this.hammer.off('rotatemove', this.onTouchRotateMove);
|
||||
this.hammer.off('rotateend', this.onTouchRotateUp);
|
||||
this.hammer.off('rotatecancel', this.onTouchRotateUp);
|
||||
window.removeEventListener('contextmenu', this.onContextMenu);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param deltaTime {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(deltaTime, map) {
|
||||
// == process mouse movements ==
|
||||
let deltaMouse = this.lastMouse.clone().sub(this.mouse);
|
||||
let moveDelta = new Vector2();
|
||||
|
||||
// zoom keys
|
||||
if (this.keyStates.ZOOM_IN) {
|
||||
this.targetDistance *= 1 - 0.003 * deltaTime;
|
||||
this.updateZoom();
|
||||
}
|
||||
if (this.keyStates.ZOOM_OUT){
|
||||
this.targetDistance *= 1 + 0.003 * deltaTime;
|
||||
this.updateZoom();
|
||||
}
|
||||
|
||||
// move
|
||||
if (this.state === MapControls.STATES.MOVE) {
|
||||
moveDelta.copy(deltaMouse);
|
||||
} else {
|
||||
if (this.keyStates.UP) moveDelta.y -= 20;
|
||||
if (this.keyStates.DOWN) moveDelta.y += 20;
|
||||
if (this.keyStates.LEFT) moveDelta.x -= 20;
|
||||
if (this.keyStates.RIGHT) moveDelta.x += 20;
|
||||
}
|
||||
|
||||
if (moveDelta.x !== 0 || moveDelta.y !== 0) {
|
||||
moveDelta.rotateAround(MapControls.VECTOR2_ZERO, this.controls.rotation);
|
||||
this.targetPosition.set(
|
||||
this.targetPosition.x + (moveDelta.x * this.targetDistance / this.rootElement.clientHeight * 1.5),
|
||||
this.targetPosition.y,
|
||||
this.targetPosition.z + (moveDelta.y * this.targetDistance / this.rootElement.clientHeight * 1.5)
|
||||
);
|
||||
}
|
||||
|
||||
this.updatePositionTerrainHeight(map);
|
||||
|
||||
// tilt/pan
|
||||
if (this.state === MapControls.STATES.ORBIT) {
|
||||
if (deltaMouse.x !== 0) {
|
||||
this.targetRotation -= (deltaMouse.x / this.rootElement.clientHeight * Math.PI);
|
||||
this.wrapRotation();
|
||||
}
|
||||
|
||||
if (deltaMouse.y !== 0) {
|
||||
this.targetAngle += (deltaMouse.y / this.rootElement.clientHeight * Math.PI);
|
||||
this.targetAngle = MathUtils.clamp(this.targetAngle, this.minAngle, this.maxAngleForZoom + 0.1);
|
||||
}
|
||||
}
|
||||
if (this.targetAngle > this.maxAngleForZoom) this.targetAngle -= (this.targetAngle - this.maxAngleForZoom) * 0.3;
|
||||
|
||||
// == Smoothly apply target values ==
|
||||
let somethingChanged = false;
|
||||
|
||||
// move
|
||||
let deltaPosition = this.targetPosition.clone().sub(this.controls.position);
|
||||
if (Math.abs(deltaPosition.x) > 0.01 || Math.abs(deltaPosition.y) > 0.001 || Math.abs(deltaPosition.z) > 0.01) {
|
||||
this.controls.position = this.controls.position.add(deltaPosition.multiplyScalar(0.015 * deltaTime));
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
// rotation
|
||||
let deltaRotation = this.targetRotation - this.controls.rotation;
|
||||
if (Math.abs(deltaRotation) > 0.0001) {
|
||||
this.controls.rotation += deltaRotation * 0.015 * deltaTime;
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
// angle
|
||||
let deltaAngle = this.targetAngle - this.controls.angle;
|
||||
if (Math.abs(deltaAngle) > 0.0001) {
|
||||
this.controls.angle += deltaAngle * 0.015 * deltaTime;
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
// zoom
|
||||
let deltaDistance = this.targetDistance - this.controls.distance
|
||||
if (Math.abs(deltaDistance) > 0.001) {
|
||||
this.controls.distance += deltaDistance * 0.01 * deltaTime;
|
||||
somethingChanged = true;
|
||||
}
|
||||
|
||||
// == Adjust camera height to terrain ==
|
||||
if (somethingChanged) {
|
||||
let y = 0;
|
||||
if (this.positionTerrainHeight !== false) {
|
||||
y = this.targetPosition.y;
|
||||
let deltaY = this.positionTerrainHeight - y;
|
||||
if (Math.abs(deltaY) > 0.001) {
|
||||
y += deltaY * 0.01 * deltaTime;
|
||||
}
|
||||
}
|
||||
let minCameraHeight = map.terrainHeightAt(this.controls.camera.position.x, this.controls.camera.position.z) + ((this.minDistance - this.targetDistance) * 0.4) + 1;
|
||||
if (minCameraHeight > y) y = minCameraHeight;
|
||||
this.targetPosition.y = y;
|
||||
}
|
||||
|
||||
// == Fix NaN's as a fail-safe ==
|
||||
if (isNaN(this.targetPosition.x)){
|
||||
alert(this.events, `Invalid targetPosition x: ${this.targetPosition.x}`, "warning");
|
||||
this.targetPosition.x = 0;
|
||||
}
|
||||
if (isNaN(this.targetPosition.y)){
|
||||
alert(this.events, `Invalid targetPosition y: ${this.targetPosition.y}`, "warning");
|
||||
this.targetPosition.y = 0;
|
||||
}
|
||||
if (isNaN(this.targetPosition.z)){
|
||||
alert(this.events, `Invalid targetPosition z: ${this.targetPosition.z}`, "warning");
|
||||
this.targetPosition.z = 0;
|
||||
}
|
||||
if (isNaN(this.targetDistance)){
|
||||
alert(this.events, `Invalid targetDistance: ${this.targetDistance}`, "warning");
|
||||
this.targetDistance = this.minDistance;
|
||||
}
|
||||
if (isNaN(this.targetRotation)){
|
||||
alert(this.events, `Invalid targetRotation: ${this.targetRotation}`, "warning");
|
||||
this.targetRotation = 0;
|
||||
}
|
||||
if (isNaN(this.targetAngle)){
|
||||
alert(this.events, `Invalid targetAngle: ${this.targetAngle}`, "warning");
|
||||
this.targetAngle = this.minAngle;
|
||||
}
|
||||
|
||||
// == Remember last processed state ==
|
||||
this.lastMouse.copy(this.mouse);
|
||||
}
|
||||
|
||||
updateZoom() {
|
||||
this.targetDistance = MathUtils.clamp(this.targetDistance, this.minDistance, this.maxDistance);
|
||||
this.updateMaxAngleForZoom();
|
||||
this.targetAngle = MathUtils.clamp(this.targetAngle, this.minAngle, this.maxAngleForZoom);
|
||||
}
|
||||
|
||||
updateMaxAngleForZoom() {
|
||||
this.maxAngleForZoom =
|
||||
MathUtils.clamp(
|
||||
(1 - Math.pow((this.targetDistance - this.minDistance) / (500 - this.minDistance), 0.5)) * this.maxAngle,
|
||||
this.minAngle,
|
||||
this.maxAngle
|
||||
);
|
||||
}
|
||||
|
||||
updatePositionTerrainHeight(map) {
|
||||
this.positionTerrainHeight = map.terrainHeightAt(this.targetPosition.x, this.targetPosition.z);
|
||||
}
|
||||
|
||||
wrapRotation() {
|
||||
while (this.targetRotation >= Math.PI) {
|
||||
this.targetRotation -= Math.PI * 2;
|
||||
this.controls.rotation -= Math.PI * 2;
|
||||
}
|
||||
while (this.targetRotation <= -Math.PI) {
|
||||
this.targetRotation += Math.PI * 2;
|
||||
this.controls.rotation += Math.PI * 2;
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown = evt => {
|
||||
let key = evt.key || evt.keyCode;
|
||||
for (let action in MapControls.KEYS){
|
||||
if (!MapControls.KEYS.hasOwnProperty(action)) continue;
|
||||
if (MapControls.KEYS[action].includes(key)){
|
||||
this.keyStates[action] = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onKeyUp = evt => {
|
||||
let key = evt.key || evt.keyCode;
|
||||
for (let action in MapControls.KEYS){
|
||||
if (!MapControls.KEYS.hasOwnProperty(action)) continue;
|
||||
if (MapControls.KEYS[action].includes(key)){
|
||||
this.keyStates[action] = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onWheel = evt => {
|
||||
let delta = evt.deltaY;
|
||||
if (evt.deltaMode === WheelEvent.DOM_DELTA_PIXEL) delta *= 0.01;
|
||||
if (evt.deltaMode === WheelEvent.DOM_DELTA_LINE) delta *= 0.33;
|
||||
|
||||
this.targetDistance *= Math.pow(1.5, delta);
|
||||
this.updateZoom();
|
||||
}
|
||||
|
||||
onMouseDown = evt => {
|
||||
if (this.state !== MapControls.STATES.NONE) return;
|
||||
|
||||
if (MapControls.BUTTONS.MOVE.includes(evt.button)) {
|
||||
this.state = MapControls.STATES.MOVE;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (MapControls.BUTTONS.ORBIT.includes(evt.button)) {
|
||||
this.state = MapControls.STATES.ORBIT;
|
||||
evt.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
onMouseMove = evt => {
|
||||
this.mouse.set(evt.clientX, evt.clientY);
|
||||
|
||||
if (this.state !== MapControls.STATES.NONE){
|
||||
evt.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
onMouseUp = evt => {
|
||||
if (this.state === MapControls.STATES.NONE) return;
|
||||
|
||||
if (MapControls.BUTTONS.MOVE.includes(evt.button)) {
|
||||
if (this.state === MapControls.STATES.MOVE) this.state = MapControls.STATES.NONE;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (MapControls.BUTTONS.ORBIT.includes(evt.button)) {
|
||||
if (this.state === MapControls.STATES.ORBIT) this.state = MapControls.STATES.NONE;
|
||||
evt.preventDefault();
|
||||
}
|
||||
};
|
||||
|
||||
onTouchDown = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
this.touchStart.set(this.targetPosition.x, this.targetPosition.z);
|
||||
this.state = MapControls.STATES.MOVE;
|
||||
};
|
||||
|
||||
onTouchMove = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
if (this.state !== MapControls.STATES.MOVE) return;
|
||||
|
||||
let touchDelta = new Vector2(evt.deltaX, evt.deltaY);
|
||||
|
||||
if (touchDelta.x !== 0 || touchDelta.y !== 0) {
|
||||
touchDelta.rotateAround(MapControls.VECTOR2_ZERO, this.controls.rotation);
|
||||
|
||||
this.targetPosition.x = this.touchStart.x - (touchDelta.x * this.targetDistance / this.rootElement.clientHeight * 1.5);
|
||||
this.targetPosition.z = this.touchStart.y - (touchDelta.y * this.targetDistance / this.rootElement.clientHeight * 1.5);
|
||||
}
|
||||
};
|
||||
|
||||
onTouchUp = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
this.state = MapControls.STATES.NONE;
|
||||
};
|
||||
|
||||
onTouchTiltDown = () => {
|
||||
this.touchTiltStart = this.targetAngle;
|
||||
this.state = MapControls.STATES.ORBIT;
|
||||
};
|
||||
|
||||
onTouchTiltMove = evt => {
|
||||
if (this.state !== MapControls.STATES.ORBIT) return;
|
||||
|
||||
this.targetAngle = this.touchTiltStart - (evt.deltaY / this.rootElement.clientHeight * Math.PI);
|
||||
this.targetAngle = MathUtils.clamp(this.targetAngle, this.minAngle, this.maxAngleForZoom + 0.1);
|
||||
};
|
||||
|
||||
onTouchTiltUp = () => {
|
||||
this.state = MapControls.STATES.NONE;
|
||||
};
|
||||
|
||||
onTouchRotateDown = evt => {
|
||||
this.lastTouchRotation = evt.rotation;
|
||||
this.state = MapControls.STATES.ORBIT;
|
||||
};
|
||||
|
||||
onTouchRotateMove = evt => {
|
||||
if (this.state !== MapControls.STATES.ORBIT) return;
|
||||
|
||||
let delta = evt.rotation - this.lastTouchRotation;
|
||||
this.lastTouchRotation = evt.rotation;
|
||||
if (delta > 180) delta -= 360;
|
||||
if (delta < -180) delta += 360;
|
||||
|
||||
this.targetRotation -= (delta * (Math.PI / 180)) * 1.4;
|
||||
this.wrapRotation();
|
||||
};
|
||||
|
||||
onTouchRotateUp = () => {
|
||||
this.state = MapControls.STATES.NONE;
|
||||
};
|
||||
|
||||
onTouchZoomDown = () => {
|
||||
this.touchZoomStart = this.targetDistance;
|
||||
};
|
||||
|
||||
onTouchZoomMove = evt => {
|
||||
this.targetDistance = this.touchZoomStart / evt.scale;
|
||||
this.updateZoom();
|
||||
};
|
||||
|
||||
onContextMenu = evt => {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
import {animate, EasingFunctions, softMin} from "../../util/Utils";
|
||||
import {KeyMoveControls} from "./keyboard/KeyMoveControls";
|
||||
import {MouseRotateControls} from "./mouse/MouseRotateControls";
|
||||
import {MouseAngleControls} from "./mouse/MouseAngleControls";
|
||||
import {KeyHeightControls} from "./keyboard/KeyHeightControls";
|
||||
import {TouchPanControls} from "./touch/TouchPanControls";
|
||||
|
||||
export class FreeFlightControls {
|
||||
|
||||
/**
|
||||
* @param target {Element}
|
||||
*/
|
||||
constructor(target) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.hammer = new Hammer.Manager(this.target);
|
||||
this.initializeHammer();
|
||||
|
||||
this.keyMove = new KeyMoveControls(this.target, 0.5, 0.1);
|
||||
this.keyHeight = new KeyHeightControls(this.target, 0.5, 0.2);
|
||||
this.mouseRotate = new MouseRotateControls(this.target, 0.002, -0.003, -0.002, 0.5);
|
||||
this.mouseAngle = new MouseAngleControls(this.target, 0.002, -0.003, -0.002, 0.5);
|
||||
this.touchPan = new TouchPanControls(this.hammer, 0.005, 0.15);
|
||||
|
||||
this.started = false;
|
||||
|
||||
this.clickStart = new Vector2();
|
||||
this.moveSpeed = 0.5;
|
||||
|
||||
this.animationTargetHeight = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.keyMove.start(manager);
|
||||
this.keyHeight.start(manager);
|
||||
this.mouseRotate.start(manager);
|
||||
this.mouseAngle.start(manager);
|
||||
this.touchPan.start(manager);
|
||||
|
||||
this.target.addEventListener("contextmenu", this.onContextMenu);
|
||||
this.target.addEventListener("mousedown", this.onMouseDown);
|
||||
this.target.addEventListener("mouseup", this.onMouseUp);
|
||||
window.addEventListener("wheel", this.onWheel, {passive: true});
|
||||
|
||||
let startOrtho = this.manager.ortho;
|
||||
let startDistance = this.manager.distance;
|
||||
let startAngle = this.manager.angle;
|
||||
let startY = this.manager.position.y;
|
||||
|
||||
let targetAngle = Math.PI / 2;
|
||||
|
||||
animate(progress => {
|
||||
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
|
||||
|
||||
this.manager.ortho = MathUtils.lerp(startOrtho, 0, progress);
|
||||
this.manager.distance = MathUtils.lerp(startDistance, 0, smoothProgress);
|
||||
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
|
||||
this.manager.position.y = MathUtils.lerp(startY, this.animationTargetHeight, smoothProgress);
|
||||
}, 500, () => {
|
||||
this.started = true;
|
||||
});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.keyMove.stop();
|
||||
this.keyHeight.stop();
|
||||
this.mouseRotate.stop();
|
||||
this.mouseAngle.stop();
|
||||
this.touchPan.stop();
|
||||
|
||||
this.started = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (!this.started) {
|
||||
this.animationTargetHeight = map.terrainHeightAt(this.manager.position.x, this.manager.position.z) + 10;
|
||||
return;
|
||||
}
|
||||
|
||||
this.keyMove.update(delta, map);
|
||||
this.keyHeight.update(delta, map);
|
||||
this.mouseRotate.update(delta, map);
|
||||
this.mouseAngle.update(delta, map);
|
||||
this.touchPan.update(delta, map);
|
||||
|
||||
this.manager.angle = MathUtils.clamp(this.manager.angle, 0, Math.PI);
|
||||
}
|
||||
|
||||
initializeHammer() {
|
||||
let touchTap = new Hammer.Tap({ event: 'tap', pointers: 1, taps: 1, threshold: 2 });
|
||||
let touchMove = new Hammer.Pan({ event: 'move', pointers: 1, direction: Hammer.DIRECTION_ALL, threshold: 0 });
|
||||
let touchTilt = new Hammer.Pan({ event: 'tilt', pointers: 2, direction: Hammer.DIRECTION_VERTICAL, threshold: 0 });
|
||||
let touchRotate = new Hammer.Rotate({ event: 'rotate', pointers: 2, threshold: 0 });
|
||||
let touchZoom = new Hammer.Pinch({ event: 'zoom', pointers: 2, threshold: 0 });
|
||||
|
||||
touchMove.recognizeWith(touchRotate);
|
||||
touchMove.recognizeWith(touchTilt);
|
||||
touchMove.recognizeWith(touchZoom);
|
||||
touchTilt.recognizeWith(touchRotate);
|
||||
touchTilt.recognizeWith(touchZoom);
|
||||
touchRotate.recognizeWith(touchZoom);
|
||||
|
||||
this.hammer.add(touchTap);
|
||||
this.hammer.add(touchTilt);
|
||||
this.hammer.add(touchMove);
|
||||
this.hammer.add(touchRotate);
|
||||
this.hammer.add(touchZoom);
|
||||
}
|
||||
|
||||
onContextMenu = evt => {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
onMouseDown = evt => {
|
||||
this.clickStart.set(evt.x, evt.y);
|
||||
}
|
||||
|
||||
onMouseUp = evt => {
|
||||
if (this.clickStart.x !== evt.x) return;
|
||||
if (this.clickStart.y !== evt.y) return;
|
||||
|
||||
this.target.requestPointerLock();
|
||||
}
|
||||
|
||||
onWheel = evt => {
|
||||
let delta = evt.deltaY;
|
||||
if (evt.deltaMode === WheelEvent.DOM_DELTA_PIXEL) delta *= 0.01;
|
||||
if (evt.deltaMode === WheelEvent.DOM_DELTA_LINE) delta *= 0.33;
|
||||
|
||||
this.moveSpeed *= Math.pow(1.5, -delta * 0.25);
|
||||
this.moveSpeed = MathUtils.clamp(this.moveSpeed, 0.05, 5);
|
||||
|
||||
this.keyMove.speed = this.moveSpeed;
|
||||
this.keyHeight.speed = this.moveSpeed;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
import {MathUtils} from "three";
|
||||
import {KeyCombination} from "../../KeyCombination";
|
||||
|
||||
export class KeyHeightControls {
|
||||
|
||||
static KEYS = {
|
||||
UP: [
|
||||
new KeyCombination("Space"),
|
||||
new KeyCombination("PageUp")
|
||||
],
|
||||
DOWN: [
|
||||
new KeyCombination("ShiftLeft"),
|
||||
new KeyCombination("PageDown")
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.deltaY = 0;
|
||||
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.up) this.deltaY += 1;
|
||||
if (this.down) this.deltaY -= 1;
|
||||
|
||||
if (this.deltaY === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.position.y += this.deltaY * smoothing * this.speed * delta * 0.06;
|
||||
|
||||
this.deltaY *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaY) < 0.0001) {
|
||||
this.deltaY = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyDown = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyHeightControls.KEYS.UP)){
|
||||
this.up = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
else if (KeyCombination.oneUp(evt, ...KeyHeightControls.KEYS.DOWN)){
|
||||
this.down = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyUp = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyHeightControls.KEYS.UP)){
|
||||
this.up = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyHeightControls.KEYS.DOWN)){
|
||||
this.down = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
import {VEC2_ZERO} from "../../../util/Utils";
|
||||
import {KeyCombination} from "../../KeyCombination";
|
||||
|
||||
export class KeyMoveControls {
|
||||
|
||||
static KEYS = {
|
||||
LEFT: [
|
||||
new KeyCombination("ArrowLeft"),
|
||||
new KeyCombination("KeyA")
|
||||
],
|
||||
UP: [
|
||||
new KeyCombination("ArrowUp"),
|
||||
new KeyCombination("KeyW")
|
||||
],
|
||||
RIGHT: [
|
||||
new KeyCombination("ArrowRight"),
|
||||
new KeyCombination("KeyD")
|
||||
],
|
||||
DOWN: [
|
||||
new KeyCombination("ArrowDown"),
|
||||
new KeyCombination("KeyS")
|
||||
],
|
||||
}
|
||||
|
||||
static temp_v2 = new Vector2();
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.deltaPosition = new Vector2();
|
||||
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
this.left = false;
|
||||
this.right = false;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.up) this.deltaPosition.y -= 1;
|
||||
if (this.down) this.deltaPosition.y += 1;
|
||||
if (this.left) this.deltaPosition.x -= 1;
|
||||
if (this.right) this.deltaPosition.x += 1;
|
||||
|
||||
if (this.deltaPosition.x === 0 && this.deltaPosition.y === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
let rotatedDelta = KeyMoveControls.temp_v2.copy(this.deltaPosition);
|
||||
rotatedDelta.rotateAround(VEC2_ZERO, this.manager.rotation);
|
||||
|
||||
this.manager.position.x += rotatedDelta.x * smoothing * this.speed * delta * 0.06;
|
||||
this.manager.position.z += rotatedDelta.y * smoothing * this.speed * delta * 0.06;
|
||||
|
||||
this.deltaPosition.multiplyScalar(1 - smoothing);
|
||||
if (this.deltaPosition.lengthSq() < 0.0001) {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyDown = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.UP)){
|
||||
this.up = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.DOWN)){
|
||||
this.down = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.LEFT)){
|
||||
this.left = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.RIGHT)){
|
||||
this.right = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyUp = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.UP)){
|
||||
this.up = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.DOWN)){
|
||||
this.down = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.LEFT)){
|
||||
this.left = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.RIGHT)){
|
||||
this.right = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class MouseAngleControls {
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speedLeft {number}
|
||||
* @param speedRight {number}
|
||||
* @param speedCapture {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speedLeft, speedRight, speedCapture, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastY = 0;
|
||||
this.deltaAngle = 0;
|
||||
|
||||
this.speedLeft = speedLeft;
|
||||
this.speedRight = speedRight;
|
||||
this.speedCapture = speedCapture;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.target.addEventListener("mousedown", this.onMouseDown);
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.target.removeEventListener("mousedown", this.onMouseDown);
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaAngle === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.angle += this.deltaAngle * smoothing;
|
||||
|
||||
this.deltaAngle *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaAngle) < 0.0001) {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseDown = evt => {
|
||||
this.moving = true;
|
||||
this.deltaAngle = 0;
|
||||
this.lastY = evt.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseMove = evt => {
|
||||
if (document.pointerLockElement) {
|
||||
this.deltaAngle += evt.movementY * this.speedCapture;
|
||||
}
|
||||
|
||||
else if(this.moving){
|
||||
if (evt.buttons === 1) {
|
||||
this.deltaAngle += (evt.y - this.lastY) * this.speedLeft;
|
||||
} else {
|
||||
this.deltaAngle += (evt.y - this.lastY) * this.speedRight;
|
||||
}
|
||||
}
|
||||
|
||||
this.lastY = evt.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class MouseRotateControls {
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speedLeft {number}
|
||||
* @param speedRight {number}
|
||||
* @param speedCapture {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speedLeft, speedRight, speedCapture, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastX = 0;
|
||||
this.deltaRotation = 0;
|
||||
|
||||
this.speedLeft = speedLeft;
|
||||
this.speedRight = speedRight;
|
||||
this.speedCapture = speedCapture;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.target.addEventListener("mousedown", this.onMouseDown);
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.target.removeEventListener("mousedown", this.onMouseDown);
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaRotation === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.rotation += this.deltaRotation * smoothing;
|
||||
|
||||
this.deltaRotation *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaRotation) < 0.0001) {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseDown = evt => {
|
||||
this.moving = true;
|
||||
this.deltaRotation = 0;
|
||||
this.lastX = evt.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseMove = evt => {
|
||||
if (document.pointerLockElement) {
|
||||
this.deltaRotation -= evt.movementX * this.speedCapture;
|
||||
}
|
||||
|
||||
else if(this.moving){
|
||||
if (evt.buttons === 1) {
|
||||
this.deltaRotation -= (evt.x - this.lastX) * this.speedLeft;
|
||||
} else {
|
||||
this.deltaRotation -= (evt.x - this.lastX) * this.speedRight;
|
||||
}
|
||||
}
|
||||
|
||||
this.lastX = evt.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
|
||||
export class TouchPanControls {
|
||||
|
||||
static tempVec2_1 = new Vector2();
|
||||
|
||||
/**
|
||||
* @param hammer {Hammer.Manager}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(hammer, speed, stiffness) {
|
||||
this.hammer = hammer;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastPosition = new Vector2();
|
||||
this.deltaPosition = new Vector2();
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.hammer.on("movestart", this.onTouchDown);
|
||||
this.hammer.on("movemove", this.onTouchMove);
|
||||
this.hammer.on("moveend", this.onTouchUp);
|
||||
this.hammer.on("movecancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.hammer.off("movestart", this.onTouchDown);
|
||||
this.hammer.off("movemove", this.onTouchMove);
|
||||
this.hammer.off("moveend", this.onTouchUp);
|
||||
this.hammer.off("movecancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaPosition.x === 0 && this.deltaPosition.y === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.rotation += this.deltaPosition.x * this.speed * this.stiffness;
|
||||
this.manager.angle -= this.deltaPosition.y * this.speed * this.stiffness;
|
||||
|
||||
this.deltaPosition.multiplyScalar(1 - smoothing);
|
||||
if (this.deltaPosition.lengthSq() < 0.0001) {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchDown = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
this.moving = true;
|
||||
this.deltaPosition.set(0, 0);
|
||||
this.lastPosition.set(evt.center.x, evt.center.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchMove = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
let position = TouchPanControls.tempVec2_1.set(evt.center.x, evt.center.y);
|
||||
|
||||
if(this.moving){
|
||||
this.deltaPosition.sub(position).add(this.lastPosition);
|
||||
}
|
||||
|
||||
this.lastPosition.copy(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchUp = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
import {MouseMoveControls} from "./mouse/MouseMoveControls";
|
||||
import {MouseZoomControls} from "./mouse/MouseZoomControls";
|
||||
import {MouseRotateControls} from "./mouse/MouseRotateControls";
|
||||
import {MouseAngleControls} from "./mouse/MouseAngleControls";
|
||||
import {MathUtils} from "three";
|
||||
import {animate, EasingFunctions, softClamp, softMin} from "../../util/Utils";
|
||||
import {MapHeightControls} from "./MapHeightControls";
|
||||
import {KeyMoveControls} from "./keyboard/KeyMoveControls";
|
||||
import {KeyAngleControls} from "./keyboard/KeyAngleControls";
|
||||
import {KeyRotateControls} from "./keyboard/KeyRotateControls";
|
||||
import {KeyZoomControls} from "./keyboard/KeyZoomControls";
|
||||
import {TouchMoveControls} from "./touch/TouchMoveControls";
|
||||
import {TouchRotateControls} from "./touch/TouchRotateControls";
|
||||
import {TouchAngleControls} from "./touch/TouchAngleControls";
|
||||
import {TouchZoomControls} from "./touch/TouchZoomControls";
|
||||
|
||||
const HALF_PI = Math.PI * 0.5;
|
||||
|
||||
export class MapControls {
|
||||
|
||||
/**
|
||||
* @param rootElement {EventTarget}
|
||||
*/
|
||||
constructor(rootElement) {
|
||||
this.rootElement = rootElement;
|
||||
this.manager = null;
|
||||
|
||||
this.started = false;
|
||||
|
||||
this.hammer = new Hammer.Manager(this.rootElement);
|
||||
this.initializeHammer();
|
||||
|
||||
//controls
|
||||
this.mouseMove = new MouseMoveControls(this.rootElement, 0.002,0.3);
|
||||
this.mouseRotate = new MouseRotateControls(this.rootElement, 0.004, 0.3);
|
||||
this.mouseAngle = new MouseAngleControls(this.rootElement, 0.004, 0.3);
|
||||
this.mouseZoom = new MouseZoomControls(this.rootElement, 1, 0.2);
|
||||
|
||||
this.keyMove = new KeyMoveControls(this.rootElement, 0.025, 0.2);
|
||||
this.keyRotate = new KeyRotateControls(this.rootElement, 0.06, 0.15);
|
||||
this.keyAngle = new KeyAngleControls(this.rootElement, 0.04, 0.15);
|
||||
this.keyZoom = new KeyZoomControls(this.rootElement, 0.2, 0.15);
|
||||
|
||||
this.touchMove = new TouchMoveControls(this.hammer, 0.002,0.3);
|
||||
this.touchRotate = new TouchRotateControls(this.hammer, 0.0174533, 0.3);
|
||||
this.touchAngle = new TouchAngleControls(this.hammer, 0.01, 0.3);
|
||||
this.touchZoom = new TouchZoomControls(this.hammer);
|
||||
|
||||
this.mapHeight = new MapHeightControls(0.2, 0.1);
|
||||
|
||||
this.animationTargetHeight = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.rootElement.addEventListener("contextmenu", this.onContextMenu);
|
||||
|
||||
this.mouseMove.start(manager);
|
||||
this.mouseRotate.start(manager);
|
||||
this.mouseAngle.start(manager);
|
||||
this.mouseZoom.start(manager);
|
||||
|
||||
this.keyMove.start(manager);
|
||||
this.keyRotate.start(manager);
|
||||
this.keyAngle.start(manager);
|
||||
this.keyZoom.start(manager);
|
||||
|
||||
this.touchMove.start(manager);
|
||||
this.touchRotate.start(manager);
|
||||
this.touchAngle.start(manager);
|
||||
this.touchZoom.start(manager);
|
||||
|
||||
this.mapHeight.start(manager);
|
||||
|
||||
|
||||
let startOrtho = this.manager.ortho;
|
||||
let startDistance = this.manager.distance;
|
||||
let startAngle = this.manager.angle;
|
||||
let startY = this.manager.position.y;
|
||||
|
||||
let targetDistance = MathUtils.clamp(this.manager.distance, 100, 10000);
|
||||
let targetAngle = Math.min(startAngle, this.getMaxPerspectiveAngleForDistance(targetDistance));
|
||||
|
||||
animate(progress => {
|
||||
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
|
||||
|
||||
this.manager.ortho = MathUtils.lerp(startOrtho, 0, progress);
|
||||
this.manager.distance = MathUtils.lerp(startDistance, targetDistance, smoothProgress);
|
||||
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
|
||||
this.manager.position.y = MathUtils.lerp(startY, this.animationTargetHeight, smoothProgress);
|
||||
}, 500, () => this.started = true);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.rootElement.removeEventListener("contextmenu", this.onContextMenu);
|
||||
|
||||
this.mouseMove.stop();
|
||||
this.mouseRotate.stop();
|
||||
this.mouseAngle.stop();
|
||||
this.mouseZoom.stop();
|
||||
|
||||
this.keyMove.stop();
|
||||
this.keyRotate.stop();
|
||||
this.keyAngle.stop();
|
||||
this.keyZoom.stop();
|
||||
|
||||
this.touchMove.stop();
|
||||
this.touchRotate.stop();
|
||||
this.touchAngle.stop();
|
||||
this.touchZoom.stop();
|
||||
|
||||
this.mapHeight.stop();
|
||||
|
||||
this.started = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (!this.started){
|
||||
this.mapHeight.updateHeights(delta, map);
|
||||
this.animationTargetHeight = this.mapHeight.getSuggestedHeight();
|
||||
return;
|
||||
}
|
||||
|
||||
// move and zoom
|
||||
this.mouseMove.update(delta, map);
|
||||
this.mouseZoom.update(delta, map);
|
||||
this.keyMove.update(delta, map);
|
||||
this.keyZoom.update(delta, map);
|
||||
this.touchMove.update(delta, map);
|
||||
this.touchZoom.update(delta, map);
|
||||
|
||||
this.manager.distance = softClamp(this.manager.distance, 5, 10000, 0.8);
|
||||
|
||||
// max angle for current distance
|
||||
let maxAngleForZoom = this.getMaxPerspectiveAngleForDistance(this.manager.distance);
|
||||
|
||||
// rotation
|
||||
if (this.manager.ortho === 0) {
|
||||
this.mouseRotate.update(delta, map);
|
||||
this.keyRotate.update(delta, map);
|
||||
this.touchRotate.update(delta, map);
|
||||
}
|
||||
|
||||
// tilt
|
||||
if (this.manager.ortho === 0) {
|
||||
this.mouseAngle.update(delta, map);
|
||||
this.keyAngle.update(delta, map);
|
||||
this.touchAngle.update(delta, map);
|
||||
this.manager.angle = softClamp(this.manager.angle, 0, maxAngleForZoom, 0.8);
|
||||
}
|
||||
|
||||
// target height
|
||||
if (this.manager.ortho === 0 || this.manager.angle === 0) {
|
||||
this.manager.position.y = 0;
|
||||
this.mapHeight.maxAngle = maxAngleForZoom;
|
||||
this.mapHeight.update(delta, map);
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.mouseMove.reset();
|
||||
this.mouseRotate.reset();
|
||||
this.mouseAngle.reset();
|
||||
this.mouseZoom.reset();
|
||||
|
||||
this.touchMove.reset();
|
||||
this.touchRotate.reset();
|
||||
this.touchAngle.reset();
|
||||
this.touchZoom.reset();
|
||||
}
|
||||
|
||||
getMaxPerspectiveAngleForDistance(distance) {
|
||||
return MathUtils.clamp((1 - Math.pow(Math.max(distance - 5, 0.001) / 500, 0.5)) * HALF_PI,0, HALF_PI)
|
||||
}
|
||||
|
||||
setPerspectiveView() {
|
||||
this.reset();
|
||||
|
||||
let startOrtho = this.manager.ortho;
|
||||
let startAngle = this.manager.angle;
|
||||
|
||||
let targetAngle = Math.min(startAngle, this.getMaxPerspectiveAngleForDistance(this.manager.distance));
|
||||
|
||||
animate(progress => {
|
||||
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
|
||||
|
||||
this.manager.ortho = MathUtils.lerp(startOrtho, 0, progress);
|
||||
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
setOrthographicView(targetRotation = 0, targetAngle = 0) {
|
||||
this.reset();
|
||||
|
||||
let startOrtho = this.manager.ortho;
|
||||
let startAngle = this.manager.angle;
|
||||
let startRotation = this.manager.rotation;
|
||||
|
||||
animate(progress => {
|
||||
let smoothProgress = EasingFunctions.easeInOutQuad(progress);
|
||||
|
||||
this.manager.ortho = MathUtils.lerp(startOrtho, 1, progress);
|
||||
this.manager.angle = MathUtils.lerp(startAngle, targetAngle, smoothProgress);
|
||||
this.manager.rotation = MathUtils.lerp(startRotation, targetRotation, smoothProgress);
|
||||
}, 500);
|
||||
}
|
||||
|
||||
initializeHammer() {
|
||||
let touchTap = new Hammer.Tap({ event: 'tap', pointers: 1, taps: 1, threshold: 2 });
|
||||
let touchMove = new Hammer.Pan({ event: 'move', pointers: 1, direction: Hammer.DIRECTION_ALL, threshold: 0 });
|
||||
let touchTilt = new Hammer.Pan({ event: 'tilt', pointers: 2, direction: Hammer.DIRECTION_VERTICAL, threshold: 0 });
|
||||
let touchRotate = new Hammer.Rotate({ event: 'rotate', pointers: 2, threshold: 0 });
|
||||
let touchZoom = new Hammer.Pinch({ event: 'zoom', pointers: 2, threshold: 0 });
|
||||
|
||||
touchMove.recognizeWith(touchRotate);
|
||||
touchMove.recognizeWith(touchTilt);
|
||||
touchMove.recognizeWith(touchZoom);
|
||||
touchTilt.recognizeWith(touchRotate);
|
||||
touchTilt.recognizeWith(touchZoom);
|
||||
touchRotate.recognizeWith(touchZoom);
|
||||
|
||||
this.hammer.add(touchTap);
|
||||
this.hammer.add(touchTilt);
|
||||
this.hammer.add(touchMove);
|
||||
this.hammer.add(touchRotate);
|
||||
this.hammer.add(touchZoom);
|
||||
}
|
||||
|
||||
onContextMenu = evt => {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
|
||||
export class MapHeightControls {
|
||||
|
||||
/**
|
||||
* @param cameraHeightStiffness {number}
|
||||
* @param targetHeightStiffness {number}
|
||||
*/
|
||||
constructor(cameraHeightStiffness, targetHeightStiffness) {
|
||||
this.manager = null;
|
||||
|
||||
this.cameraHeightStiffness = cameraHeightStiffness;
|
||||
this.targetHeightStiffness = targetHeightStiffness;
|
||||
this.maxAngle = Math.PI / 2;
|
||||
|
||||
this.targetHeight = 0;
|
||||
this.cameraHeight = 0;
|
||||
|
||||
this.lastTarget = new Vector2();
|
||||
this.lastTargetTerrainHeight = 0;
|
||||
|
||||
this.minCameraHeight = 0;
|
||||
this.distanceTagretHeight = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
stop() {}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
|
||||
// adjust target height
|
||||
this.updateHeights(delta, map);
|
||||
this.manager.position.y = Math.max(this.manager.position.y, this.getSuggestedHeight());
|
||||
}
|
||||
|
||||
updateHeights(delta, map) {
|
||||
//target height
|
||||
let targetSmoothing = this.targetHeightStiffness / (16.666 / delta);
|
||||
targetSmoothing = MathUtils.clamp(targetSmoothing, 0, 1);
|
||||
|
||||
let targetTerrainHeight = this.lastTargetTerrainHeight;
|
||||
if (this.lastTarget.x !== this.manager.position.x || this.lastTarget.y !== this.manager.position.z){
|
||||
targetTerrainHeight = map.terrainHeightAt(this.manager.position.x, this.manager.position.z) || 0;
|
||||
this.lastTargetTerrainHeight = targetTerrainHeight;
|
||||
this.lastTarget.set(this.manager.position.x, this.manager.position.z);
|
||||
}
|
||||
|
||||
let targetDelta = targetTerrainHeight - this.targetHeight;
|
||||
this.targetHeight += targetDelta * targetSmoothing;
|
||||
if (Math.abs(targetDelta) < 0.001) this.targetHeight = targetTerrainHeight;
|
||||
|
||||
// camera height
|
||||
this.minCameraHeight = 0;
|
||||
if (this.maxAngle >= 0.1) {
|
||||
let cameraSmoothing = this.cameraHeightStiffness / (16.666 / delta);
|
||||
cameraSmoothing = MathUtils.clamp(cameraSmoothing, 0, 1);
|
||||
|
||||
let cameraTerrainHeight = map.terrainHeightAt(this.manager.camera.position.x, this.manager.camera.position.z) || 0;
|
||||
|
||||
let cameraDelta = cameraTerrainHeight - this.cameraHeight;
|
||||
this.cameraHeight += cameraDelta * cameraSmoothing;
|
||||
if (Math.abs(cameraDelta) < 0.001) this.cameraHeight = cameraTerrainHeight;
|
||||
|
||||
let maxAngleHeight = Math.cos(this.maxAngle) * this.manager.distance;
|
||||
this.minCameraHeight = this.cameraHeight - maxAngleHeight + 1;
|
||||
}
|
||||
|
||||
// adjust targetHeight by distance
|
||||
this.distanceTagretHeight = Math.max(MathUtils.lerp(this.targetHeight, 0, this.manager.distance / 500), 0);
|
||||
}
|
||||
|
||||
getSuggestedHeight() {
|
||||
return Math.max(this.distanceTagretHeight, this.minCameraHeight);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import {MathUtils} from "three";
|
||||
import {KeyCombination} from "../../KeyCombination";
|
||||
|
||||
export class KeyAngleControls {
|
||||
|
||||
static KEYS = {
|
||||
UP: [
|
||||
new KeyCombination("ArrowUp", KeyCombination.ALT),
|
||||
new KeyCombination("KeyW", KeyCombination.ALT),
|
||||
new KeyCombination("PageUp")
|
||||
],
|
||||
DOWN: [
|
||||
new KeyCombination("ArrowDown", KeyCombination.ALT),
|
||||
new KeyCombination("KeyS", KeyCombination.ALT),
|
||||
new KeyCombination("PageDown")
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.deltaAngle = 0;
|
||||
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.up) this.deltaAngle -= 1;
|
||||
if (this.down) this.deltaAngle += 1;
|
||||
|
||||
if (this.deltaAngle === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.angle += this.deltaAngle * smoothing * this.speed * delta * 0.06;
|
||||
|
||||
this.deltaAngle *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaAngle) < 0.0001) {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyDown = evt => {
|
||||
if (KeyCombination.oneDown(evt, ...KeyAngleControls.KEYS.UP)){
|
||||
this.up = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneDown(evt, ...KeyAngleControls.KEYS.DOWN)){
|
||||
this.down = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyUp = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyAngleControls.KEYS.UP)){
|
||||
this.up = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyAngleControls.KEYS.DOWN)){
|
||||
this.down = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
import {VEC2_ZERO} from "../../../util/Utils";
|
||||
import {KeyCombination} from "../../KeyCombination";
|
||||
|
||||
export class KeyMoveControls {
|
||||
|
||||
static KEYS = {
|
||||
LEFT: [
|
||||
new KeyCombination("ArrowLeft"),
|
||||
new KeyCombination("KeyA")
|
||||
],
|
||||
UP: [
|
||||
new KeyCombination("ArrowUp"),
|
||||
new KeyCombination("KeyW")
|
||||
],
|
||||
RIGHT: [
|
||||
new KeyCombination("ArrowRight"),
|
||||
new KeyCombination("KeyD")
|
||||
],
|
||||
DOWN: [
|
||||
new KeyCombination("ArrowDown"),
|
||||
new KeyCombination("KeyS")
|
||||
],
|
||||
}
|
||||
|
||||
static temp_v2 = new Vector2();
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.deltaPosition = new Vector2();
|
||||
|
||||
this.up = false;
|
||||
this.down = false;
|
||||
this.left = false;
|
||||
this.right = false;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.up) this.deltaPosition.y -= 1;
|
||||
if (this.down) this.deltaPosition.y += 1;
|
||||
if (this.left) this.deltaPosition.x -= 1;
|
||||
if (this.right) this.deltaPosition.x += 1;
|
||||
|
||||
if (this.deltaPosition.x === 0 && this.deltaPosition.y === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
let rotatedDelta = KeyMoveControls.temp_v2.copy(this.deltaPosition);
|
||||
rotatedDelta.rotateAround(VEC2_ZERO, this.manager.rotation);
|
||||
|
||||
this.manager.position.x += rotatedDelta.x * smoothing * this.manager.distance * this.speed * delta * 0.06;
|
||||
this.manager.position.z += rotatedDelta.y * smoothing * this.manager.distance * this.speed * delta * 0.06;
|
||||
|
||||
this.deltaPosition.multiplyScalar(1 - smoothing);
|
||||
if (this.deltaPosition.lengthSq() < 0.0001) {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyDown = evt => {
|
||||
if (KeyCombination.oneDown(evt, ...KeyMoveControls.KEYS.UP)){
|
||||
this.up = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneDown(evt, ...KeyMoveControls.KEYS.DOWN)){
|
||||
this.down = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneDown(evt, ...KeyMoveControls.KEYS.LEFT)){
|
||||
this.left = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneDown(evt, ...KeyMoveControls.KEYS.RIGHT)){
|
||||
this.right = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyUp = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.UP)){
|
||||
this.up = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.DOWN)){
|
||||
this.down = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.LEFT)){
|
||||
this.left = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyMoveControls.KEYS.RIGHT)){
|
||||
this.right = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
import {MathUtils} from "three";
|
||||
import {KeyCombination} from "../../KeyCombination";
|
||||
|
||||
export class KeyRotateControls {
|
||||
|
||||
static KEYS = {
|
||||
LEFT: [
|
||||
new KeyCombination("ArrowLeft", KeyCombination.ALT),
|
||||
new KeyCombination("KeyA", KeyCombination.ALT),
|
||||
new KeyCombination("Delete"),
|
||||
],
|
||||
RIGHT: [
|
||||
new KeyCombination("ArrowRight", KeyCombination.ALT),
|
||||
new KeyCombination("KeyD", KeyCombination.ALT),
|
||||
new KeyCombination("End"),
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.deltaRotation = 0;
|
||||
|
||||
this.left = false;
|
||||
this.right = false;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.left) this.deltaRotation += 1;
|
||||
if (this.right) this.deltaRotation -= 1;
|
||||
|
||||
if (this.deltaRotation === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.rotation += this.deltaRotation * smoothing * this.speed * delta * 0.06;
|
||||
|
||||
this.deltaRotation *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaRotation) < 0.0001) {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyDown = evt => {
|
||||
if (KeyCombination.oneDown(evt, ...KeyRotateControls.KEYS.LEFT)){
|
||||
this.left = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneDown(evt, ...KeyRotateControls.KEYS.RIGHT)){
|
||||
this.right = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyUp = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyRotateControls.KEYS.LEFT)){
|
||||
this.left = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyRotateControls.KEYS.RIGHT)){
|
||||
this.right = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
import {MathUtils} from "three";
|
||||
import {KeyCombination} from "../../KeyCombination";
|
||||
|
||||
export class KeyZoomControls {
|
||||
|
||||
static KEYS = {
|
||||
IN: [
|
||||
new KeyCombination("NumpadAdd"),
|
||||
],
|
||||
OUT: [
|
||||
new KeyCombination("NumpadSubtract"),
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.deltaZoom = 0;
|
||||
|
||||
this.in = false;
|
||||
this.out = false;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
window.addEventListener("keydown", this.onKeyDown);
|
||||
window.addEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
window.removeEventListener("keydown", this.onKeyDown);
|
||||
window.removeEventListener("keyup", this.onKeyUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.in) this.deltaZoom += 1;
|
||||
if (this.out) this.deltaZoom -= 1;
|
||||
|
||||
if (this.deltaZoom === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.distance *= Math.pow(1.5, this.deltaZoom * smoothing * this.speed * delta * 0.06);
|
||||
|
||||
this.deltaZoom *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaZoom) < 0.0001) {
|
||||
this.deltaZoom = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyDown = evt => {
|
||||
if (KeyCombination.oneDown(evt, ...KeyZoomControls.KEYS.IN)){
|
||||
this.in = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
if (KeyCombination.oneDown(evt, ...KeyZoomControls.KEYS.OUT)){
|
||||
this.out = true;
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param evt {KeyboardEvent}
|
||||
*/
|
||||
onKeyUp = evt => {
|
||||
if (KeyCombination.oneUp(evt, ...KeyZoomControls.KEYS.IN)){
|
||||
this.in = false;
|
||||
}
|
||||
if (KeyCombination.oneUp(evt, ...KeyZoomControls.KEYS.OUT)){
|
||||
this.out = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class MouseAngleControls {
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastY = 0;
|
||||
this.deltaAngle = 0;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.target.addEventListener("mousedown", this.onMouseDown);
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.target.removeEventListener("mousedown", this.onMouseDown);
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaAngle === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.angle += this.deltaAngle * smoothing * this.speed;
|
||||
|
||||
this.deltaAngle *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaAngle) < 0.0001) {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseDown = evt => {
|
||||
if ((evt.buttons !== undefined ? evt.buttons === 2 : evt.button === 2) ||
|
||||
((evt.altKey || evt.ctrlKey) && (evt.buttons !== undefined ? evt.buttons === 1 : evt.button === 0))) {
|
||||
this.moving = true;
|
||||
this.deltaAngle = 0;
|
||||
this.lastY = evt.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseMove = evt => {
|
||||
if(this.moving){
|
||||
this.deltaAngle -= evt.y - this.lastY;
|
||||
}
|
||||
|
||||
this.lastY = evt.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
import {VEC2_ZERO} from "../../../util/Utils";
|
||||
|
||||
export class MouseMoveControls {
|
||||
|
||||
static tempVec2_1 = new Vector2();
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastPosition = new Vector2();
|
||||
this.deltaPosition = new Vector2();
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.target.addEventListener("mousedown", this.onMouseDown);
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.target.removeEventListener("mousedown", this.onMouseDown);
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaPosition.x === 0 && this.deltaPosition.y === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
let directionDelta = MouseMoveControls.tempVec2_1.copy(this.deltaPosition);
|
||||
directionDelta.rotateAround(VEC2_ZERO, this.manager.rotation);
|
||||
|
||||
this.manager.position.x += directionDelta.x * smoothing * this.manager.distance * this.speed;
|
||||
this.manager.position.z += directionDelta.y * smoothing * this.manager.distance * this.speed;
|
||||
|
||||
this.deltaPosition.multiplyScalar(1 - smoothing);
|
||||
if (this.deltaPosition.lengthSq() < 0.0001) {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseDown = evt => {
|
||||
if ((evt.buttons !== undefined ? evt.buttons === 1 : evt.button === 0) && !evt.altKey) {
|
||||
this.moving = true;
|
||||
this.deltaPosition.set(0, 0);
|
||||
this.lastPosition.set(evt.x, evt.y);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseMove = evt => {
|
||||
let position = MouseMoveControls.tempVec2_1.set(evt.x, evt.y);
|
||||
|
||||
if(this.moving){
|
||||
this.deltaPosition.sub(position).add(this.lastPosition);
|
||||
}
|
||||
|
||||
this.lastPosition.copy(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseUp = evt => {
|
||||
if (evt.button === 0) this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class MouseRotateControls {
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastX = 0;
|
||||
this.deltaRotation = 0;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.target.addEventListener("mousedown", this.onMouseDown);
|
||||
window.addEventListener("mousemove", this.onMouseMove);
|
||||
window.addEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.target.removeEventListener("mousedown", this.onMouseDown);
|
||||
window.removeEventListener("mousemove", this.onMouseMove);
|
||||
window.removeEventListener("mouseup", this.onMouseUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaRotation === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.rotation += this.deltaRotation * smoothing * this.speed;
|
||||
|
||||
this.deltaRotation *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaRotation) < 0.0001) {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseDown = evt => {
|
||||
if ((evt.buttons !== undefined ? evt.buttons === 2 : evt.button === 2) ||
|
||||
((evt.altKey || evt.ctrlKey) && (evt.buttons !== undefined ? evt.buttons === 1 : evt.button === 0))) {
|
||||
this.moving = true;
|
||||
this.deltaRotation = 0;
|
||||
this.lastX = evt.x;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseMove = evt => {
|
||||
if(this.moving){
|
||||
this.deltaRotation += evt.x - this.lastX;
|
||||
}
|
||||
|
||||
this.lastX = evt.x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {MouseEvent}
|
||||
*/
|
||||
onMouseUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class MouseZoomControls {
|
||||
|
||||
/**
|
||||
* @param target {EventTarget}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(target, speed, stiffness) {
|
||||
this.target = target;
|
||||
this.manager = null;
|
||||
|
||||
this.stiffness = stiffness;
|
||||
this.speed = speed;
|
||||
|
||||
this.deltaZoom = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.target.addEventListener("wheel", this.onMouseWheel, {passive: true});
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.target.removeEventListener("wheel", this.onMouseWheel);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaZoom === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.distance *= Math.pow(1.5, this.deltaZoom * smoothing * this.speed);
|
||||
|
||||
this.deltaZoom *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaZoom) < 0.0001) {
|
||||
this.deltaZoom = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaZoom = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {WheelEvent}
|
||||
*/
|
||||
onMouseWheel = evt => {
|
||||
let delta = evt.deltaY;
|
||||
if (evt.deltaMode === WheelEvent.DOM_DELTA_PIXEL) delta *= 0.01;
|
||||
if (evt.deltaMode === WheelEvent.DOM_DELTA_LINE) delta *= 0.33;
|
||||
|
||||
this.deltaZoom += delta;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class TouchAngleControls {
|
||||
|
||||
/**
|
||||
* @param hammer {Hammer.Manager}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(hammer, speed, stiffness) {
|
||||
this.hammer = hammer;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastY = 0;
|
||||
this.deltaAngle = 0;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.hammer.on("tiltstart", this.onTouchDown);
|
||||
this.hammer.on("tiltmove", this.onTouchMove);
|
||||
this.hammer.on("tiltend", this.onTouchUp);
|
||||
this.hammer.on("tiltcancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.hammer.off("tiltstart", this.onTouchDown);
|
||||
this.hammer.off("tiltmove", this.onTouchMove);
|
||||
this.hammer.off("tiltend", this.onTouchUp);
|
||||
this.hammer.off("tiltcancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaAngle === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.angle += this.deltaAngle * smoothing * this.speed;
|
||||
|
||||
this.deltaAngle *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaAngle) < 0.0001) {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaAngle = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchDown = evt => {
|
||||
this.moving = true;
|
||||
this.deltaAngle = 0;
|
||||
this.lastY = evt.center.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchMove = evt => {
|
||||
if(this.moving){
|
||||
this.deltaAngle -= evt.center.y - this.lastY;
|
||||
}
|
||||
|
||||
this.lastY = evt.center.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
import {MathUtils, Vector2} from "three";
|
||||
import {VEC2_ZERO} from "../../../util/Utils";
|
||||
|
||||
export class TouchMoveControls {
|
||||
|
||||
static tempVec2_1 = new Vector2();
|
||||
|
||||
/**
|
||||
* @param hammer {Hammer.Manager}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(hammer, speed, stiffness) {
|
||||
this.hammer = hammer;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastPosition = new Vector2();
|
||||
this.deltaPosition = new Vector2();
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.hammer.on("movestart", this.onTouchDown);
|
||||
this.hammer.on("movemove", this.onTouchMove);
|
||||
this.hammer.on("moveend", this.onTouchUp);
|
||||
this.hammer.on("movecancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.hammer.off("movestart", this.onTouchDown);
|
||||
this.hammer.off("movemove", this.onTouchMove);
|
||||
this.hammer.off("moveend", this.onTouchUp);
|
||||
this.hammer.off("movecancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaPosition.x === 0 && this.deltaPosition.y === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
let directionDelta = TouchMoveControls.tempVec2_1.copy(this.deltaPosition);
|
||||
directionDelta.rotateAround(VEC2_ZERO, this.manager.rotation);
|
||||
|
||||
this.manager.position.x += directionDelta.x * smoothing * this.manager.distance * this.speed;
|
||||
this.manager.position.z += directionDelta.y * smoothing * this.manager.distance * this.speed;
|
||||
|
||||
this.deltaPosition.multiplyScalar(1 - smoothing);
|
||||
if (this.deltaPosition.lengthSq() < 0.0001) {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaPosition.set(0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchDown = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
this.moving = true;
|
||||
this.deltaPosition.set(0, 0);
|
||||
this.lastPosition.set(evt.center.x, evt.center.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchMove = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
let position = TouchMoveControls.tempVec2_1.set(evt.center.x, evt.center.y);
|
||||
|
||||
if(this.moving){
|
||||
this.deltaPosition.sub(position).add(this.lastPosition);
|
||||
}
|
||||
|
||||
this.lastPosition.copy(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchUp = evt => {
|
||||
if (evt.pointerType === "mouse") return;
|
||||
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class TouchRotateControls {
|
||||
|
||||
/**
|
||||
* @param hammer {Hammer.Manager}
|
||||
* @param speed {number}
|
||||
* @param stiffness {number}
|
||||
*/
|
||||
constructor(hammer, speed, stiffness) {
|
||||
this.hammer = hammer;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.lastRotation = 0;
|
||||
this.deltaRotation = 0;
|
||||
|
||||
this.speed = speed;
|
||||
this.stiffness = stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.hammer.on("rotatestart", this.onTouchDown);
|
||||
this.hammer.on("rotatemove", this.onTouchMove);
|
||||
this.hammer.on("rotateend", this.onTouchUp);
|
||||
this.hammer.on("rotatecancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.hammer.off("rotatestart", this.onTouchDown);
|
||||
this.hammer.off("rotatemove", this.onTouchMove);
|
||||
this.hammer.off("rotateend", this.onTouchUp);
|
||||
this.hammer.off("rotatecancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaRotation === 0) return;
|
||||
|
||||
let smoothing = this.stiffness / (16.666 / delta);
|
||||
smoothing = MathUtils.clamp(smoothing, 0, 1);
|
||||
|
||||
this.manager.rotation += this.deltaRotation * smoothing * this.speed;
|
||||
|
||||
this.deltaRotation *= 1 - smoothing;
|
||||
if (Math.abs(this.deltaRotation) < 0.0001) {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaRotation = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchDown = evt => {
|
||||
this.moving = true;
|
||||
this.deltaRotation = 0;
|
||||
this.lastRotation = evt.rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchMove = evt => {
|
||||
if(this.moving){
|
||||
let delta = evt.rotation - this.lastRotation;
|
||||
if (delta > 180) delta -= 360;
|
||||
if (delta < -180) delta += 360;
|
||||
|
||||
this.deltaRotation -= delta;
|
||||
}
|
||||
|
||||
this.lastRotation = evt.rotation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
import {MathUtils} from "three";
|
||||
|
||||
export class TouchZoomControls {
|
||||
|
||||
/**
|
||||
* @param hammer {Hammer.Manager}
|
||||
*/
|
||||
constructor(hammer) {
|
||||
this.hammer = hammer;
|
||||
this.manager = null;
|
||||
|
||||
this.moving = false;
|
||||
this.deltaZoom = 1;
|
||||
this.lastZoom = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param manager {ControlsManager}
|
||||
*/
|
||||
start(manager) {
|
||||
this.manager = manager;
|
||||
|
||||
this.hammer.on("zoomstart", this.onTouchDown);
|
||||
this.hammer.on("zoommove", this.onTouchMove);
|
||||
this.hammer.on("zoomend", this.onTouchUp);
|
||||
this.hammer.on("zoomcancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.hammer.off("zoomstart", this.onTouchDown);
|
||||
this.hammer.off("zoommove", this.onTouchMove);
|
||||
this.hammer.off("zoomend", this.onTouchUp);
|
||||
this.hammer.off("zoomcancel", this.onTouchUp);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delta {number}
|
||||
* @param map {Map}
|
||||
*/
|
||||
update(delta, map) {
|
||||
if (this.deltaZoom === 1) return;
|
||||
|
||||
this.manager.distance /= this.deltaZoom;
|
||||
this.deltaZoom = 1;
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.deltaZoom = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchDown = evt => {
|
||||
this.moving = true;
|
||||
this.lastZoom = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchMove = evt => {
|
||||
if(this.moving){
|
||||
this.deltaZoom *= evt.scale / this.lastZoom;
|
||||
}
|
||||
|
||||
this.lastZoom = evt.scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param evt {object}
|
||||
*/
|
||||
onTouchUp = evt => {
|
||||
this.moving = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -84,7 +84,7 @@ export class LabelPopup extends CSS2DObject {
|
|||
|
||||
if (autoClose) {
|
||||
let removeHandler = evt => {
|
||||
if (evt.path.includes(this.element)) return;
|
||||
if (evt.composedPath().includes(this.element)) return;
|
||||
|
||||
inAnimation.cancel();
|
||||
this.close();
|
||||
|
|
|
@ -47,8 +47,8 @@ export class CombinedCamera extends PerspectiveCamera {
|
|||
if ( skew !== 0 ) left += near * skew / this.getFilmWidth();
|
||||
|
||||
// this part different to PerspectiveCamera
|
||||
let normalizedOrtho = -Math.pow(this.ortho - 1, 4) + 1;
|
||||
let orthoTop = this.distance * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom;
|
||||
let normalizedOrtho = -Math.pow(this.ortho - 1, 6) + 1;
|
||||
let orthoTop = Math.max(this.distance, 0.0001) * Math.tan( MathUtils.DEG2RAD * 0.5 * this.fov ) / this.zoom;
|
||||
let orthoHeight = 2 * orthoTop;
|
||||
let orthoWidth = this.aspect * orthoHeight;
|
||||
let orthoLeft = - 0.5 * orthoWidth;
|
||||
|
|
|
@ -3,8 +3,19 @@
|
|||
* @param string {string}
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
import {MathUtils} from "three";
|
||||
import {MathUtils, Vector2, Vector3} from "three";
|
||||
|
||||
export const VEC2_ZERO = new Vector2(0, 0);
|
||||
export const VEC3_ZERO = new Vector3(0, 0, 0);
|
||||
export const VEC3_X = new Vector3(1, 0, 0);
|
||||
export const VEC3_Y = new Vector3(0, 1, 0);
|
||||
export const VEC3_Z = new Vector3(0, 0, 1);
|
||||
|
||||
/**
|
||||
* Converts a url-encoded image string to an actual image-element
|
||||
* @param string {string}
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
export const stringToImage = string => {
|
||||
let image = document.createElementNS('http://www.w3.org/1999/xhtml', 'img');
|
||||
image.src = string;
|
||||
|
@ -269,3 +280,73 @@ export const deepEquals = (object1, object2) => {
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds one listener to multiple events
|
||||
* @param target {EventTarget}
|
||||
* @param types {string|string[]}
|
||||
* @param listener {EventListenerOrEventListenerObject | null}
|
||||
* @param options {boolean | AddEventListenerOptions?}
|
||||
*/
|
||||
export const addEventListeners = (target, types, listener, options) => {
|
||||
if (!Array.isArray(types)){
|
||||
types = types.trim().split(" ");
|
||||
}
|
||||
|
||||
types.forEach(type => target.addEventListener(type, listener, options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes one listener to multiple events
|
||||
* @param target {EventTarget}
|
||||
* @param types {string|string[]}
|
||||
* @param listener {EventListenerOrEventListenerObject | null}
|
||||
* @param options {boolean | EventListenerOptions?}
|
||||
*/
|
||||
export const removeEventListeners = (target, types, listener, options) => {
|
||||
if (!Array.isArray(types)){
|
||||
types = types.trim().split(" ");
|
||||
}
|
||||
|
||||
types.forEach(type => target.removeEventListener(type, listener, options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Softly clamps towards a minimum value
|
||||
* @param value {number}
|
||||
* @param min {number}
|
||||
* @param stiffness {number}
|
||||
* @returns {number}
|
||||
*/
|
||||
export const softMin = (value, min, stiffness) => {
|
||||
if (value >= min) return value;
|
||||
let delta = min - value;
|
||||
if (delta < 0.0001) return min;
|
||||
return value + delta * stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Softly clamps towards a maximum value
|
||||
* @param value {number}
|
||||
* @param max {number}
|
||||
* @param stiffness {number}
|
||||
* @returns {number}
|
||||
*/
|
||||
export const softMax = (value, max, stiffness) => {
|
||||
if (value <= max) return value;
|
||||
let delta = value - max;
|
||||
if (delta < 0.0001) return max;
|
||||
return value - delta * stiffness;
|
||||
}
|
||||
|
||||
/**
|
||||
* Softly clamps towards a minimum and maximum value
|
||||
* @param value {number}
|
||||
* @param min {number}
|
||||
* @param max {number}
|
||||
* @param stiffness {number}
|
||||
* @returns {number}
|
||||
*/
|
||||
export const softClamp = (value, min, max, stiffness) => {
|
||||
return softMax(softMin(value, min, stiffness), max, stiffness);
|
||||
}
|
Loading…
Reference in New Issue