BlueMapWeb/src/controls/freeflight/FreeFlightControls.js

162 lines
5.7 KiB
JavaScript

/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
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.target.removeEventListener("contextmenu", this.onContextMenu);
this.target.removeEventListener("mousedown", this.onMouseDown);
this.target.removeEventListener("mouseup", this.onMouseUp);
window.removeEventListener("wheel", this.onWheel);
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 touchMove = new Hammer.Pan({ event: 'move', pointers: 1, direction: Hammer.DIRECTION_ALL, threshold: 0 });
this.hammer.add(touchMove);
}
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;
}
}