/* * This file is part of BlueMap, licensed under the MIT License (MIT). * * Copyright (c) Blue (Lukas Rieger) * 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; } }