"use strict"; import { Sphere } from "../bv/Sphere.js"; import { input } from "../input/input.js"; import { Key } from "../Lock.js"; import { LonLat } from "../LonLat"; import * as math from "../math"; import { Mat4 } from "../math/Mat4"; import { Quat } from "../math/Quat"; import { Ray } from "../math/Ray.js"; import { Vec3 } from "../math/Vec3"; import { Control } from "./Control.js"; /** * Mouse planet camera dragging control. * @class * @extends {Control} * @param {Object} [options] - Control options. */ class MouseNavigation extends Control { constructor(options) { super(options); this._name = "mouseNavigation"; options = options || {}; this.grabbedPoint = new Vec3(); this._eye0 = new Vec3(); this.pointOnEarth = new Vec3(); this.earthUp = new Vec3(); this.inertia = 0.007; this.grabbedSpheroid = new Sphere(); this.planet = null; this.qRot = new Quat(); this.scaleRot = 0.0; this.distDiff = 0.3; this.stepsCount = 8; this.stepsForward = null; this.stepIndex = 0; this._lmbDoubleClickActive = true; this.minSlope = options.minSlope || 0.1; this._wheelDirection = +1; this._keyLock = new Key(); } static getMovePointsFromPixelTerrain(cam, planet, stepsCount, delta, point, forward, dir) { var steps = []; var eye = cam.eye.clone(), n = cam._b.clone(), u = cam._r.clone(), v = cam._u.clone(); var a = planet.getCartesianFromPixelTerrain(point, true); if (!a) { a = planet.getCartesianFromPixelTerrain(planet.renderer.handler.getCenter(), true); } if (a) { if (!dir) { dir = Vec3.sub(a, cam.eye).normalize(); } var d = (delta * cam.eye.distance(a)) / stepsCount; if (forward) { d = -1.25 * d; } else { d *= 2; } var scaled_n = n.scaleTo(d); let slope = dir.dot(cam.eye.normal().negate()); if (slope >= 0.1) { var grabbedSpheroid = new Sphere(); grabbedSpheroid.radius = a.length(); var rotArr = [], eyeArr = []; var breaked = false; for (let i = 0; i < stepsCount; i++) { eye.addA(scaled_n); var b = new Ray(eye, dir).hitSphere(grabbedSpheroid); eyeArr[i] = eye.clone(); if (b) { rotArr[i] = new Mat4().rotateBetweenVectors(a.normal(), b.normal()); } else { breaked = true; break; } } if (!breaked) { for (let i = 0; i < stepsCount; i++) { var rot = rotArr[i]; steps[i] = {}; steps[i].eye = rot.mulVec3(eyeArr[i]); steps[i].v = rot.mulVec3(v); steps[i].u = rot.mulVec3(u); steps[i].n = rot.mulVec3(n); } } else { eye = cam.eye.clone(); for (let i = 0; i < stepsCount; i++) { steps[i] = {}; steps[i].eye = eye.addA(scaled_n).clone(); steps[i].v = v; steps[i].u = u; steps[i].n = n; } } } else { for (let i = 0; i < stepsCount; i++) { steps[i] = {}; steps[i].eye = eye.addA(dir.scaleTo(-d)).clone(); steps[i].v = v; steps[i].u = u; steps[i].n = n; } } return steps; } } onactivate() { this.renderer.events.on("mousewheel", this.onMouseWheel, this); this.renderer.events.on("lhold", this.onMouseLeftButtonDown, this); this.renderer.events.on("rhold", this.onMouseRightButtonDown, this); this.renderer.events.on("ldown", this.onMouseLeftButtonClick, this); this.renderer.events.on("lup", this.onMouseLeftButtonUp, this); this.renderer.events.on("rdown", this.onMouseRightButtonClick, this); this.renderer.events.on("draw", this.onDraw, this, -1000); this.renderer.events.on("mousemove", this.onMouseMove, this); this.renderer.events.on("mouseleave", this.onMouseLeave, this); this.renderer.events.on("mouseenter", this.onMouseEnter, this); if (this._lmbDoubleClickActive) { this.renderer.events.on("ldblclick", this.onMouseLeftButtonDoubleClick, this); } } ondeactivate() { this.renderer.events.off("mousewheel", this.onMouseWheel); this.renderer.events.off("lhold", this.onMouseLeftButtonDown); this.renderer.events.off("rhold", this.onMouseRightButtonDown); this.renderer.events.off("ldown", this.onMouseLeftButtonClick); this.renderer.events.off("lup", this.onMouseLeftButtonUp); this.renderer.events.off("rdown", this.onMouseRightButtonClick); this.renderer.events.off("draw", this.onDraw); this.renderer.events.off("ldblclick", this.onMouseLeftButtonDoubleClick); this.renderer.events.off("mouseleave", this.onMouseLeave); this.renderer.events.off("mouseenter", this.onMouseEnter); } activateDoubleClickZoom() { if (!this._lmbDoubleClickActive) { this._lmbDoubleClickActive = true; this.renderer.events.on("ldblclick", this.onMouseLeftButtonDoubleClick, this); } } deactivateDoubleClickZoom() { if (this._lmbDoubleClickActive) { this._lmbDoubleClickActive = false; this.renderer.events.off("ldblclick", this.onMouseLeftButtonDoubleClick); } } onMouseEnter(e) { const renderEvents = this.renderer.events; if (renderEvents.isKeyPressed(input.KEY_ALT)) { renderEvents.releaseKeys(); } renderEvents.updateButtonsStates(e.buttons); if (renderEvents.mouseState.leftButtonDown) { this.renderer.handler.canvas.classList.add("ogGrabbingPoiner"); } else { this.renderer.handler.canvas.classList.remove("ogGrabbingPoiner"); } } onMouseLeave(e) { if (this.renderer.events.mouseState.leftButtonDown) { this.scaleRot = 0; } this.renderer.handler.canvas.classList.remove("ogGrabbingPoiner"); } onMouseWheel(event) { if (this.stepIndex) { return; } this.planet.stopFlying(); this.stopRotation(); this._deactivate = true; this.planet.layerLock.lock(this._keyLock); //this.planet.terrainLock.lock(this._keyLock); this.planet._normalMapCreator.lock(this._keyLock); var ms = this.renderer.events.mouseState; this.stepsForward = MouseNavigation.getMovePointsFromPixelTerrain( this.renderer.activeCamera, this.planet, this.stepsCount, this.distDiff, ms, event.wheelDelta > 0, ms.direction ); this._wheelDirection = event.wheelDelta; if (this.stepsForward) { this.stepIndex = this.stepsCount; } } oninit() { this.activate(); this.renderer.events.on("keyfree", input.KEY_ALT, this.onShiftFree, this); this.renderer.events.on("keyfree", input.KEY_PRINTSCREEN, this.onShiftFree, this); } onMouseLeftButtonDoubleClick() { this.planet.stopFlying(); this.stopRotation(); var p = this.planet.getCartesianFromPixelTerrain(this.renderer.events.mouseState); if (p) { var cam = this.renderer.activeCamera; let maxAlt = cam.maxAltitude + this.planet.ellipsoid._b; let minAlt = cam.minAltitude + this.planet.ellipsoid._b; const camAlt = cam.eye.length(); var g = this.planet.ellipsoid.cartesianToLonLat(p); if (camAlt > maxAlt || camAlt < minAlt) { this.planet.flyLonLat(new LonLat(g.lon, g.lat)) return; } if (this.renderer.events.isKeyPressed(input.KEY_ALT)) { this.planet.flyLonLat( new LonLat(g.lon, g.lat, cam.eye.distance(p) * 2.0) ); } else { this.planet.flyLonLat( new LonLat(g.lon, g.lat, cam.eye.distance(p) * 0.57) ); } } } onMouseLeftButtonClick() { if (this._active) { this.renderer.handler.canvas.classList.add("ogGrabbingPoiner"); this.grabbedPoint = this.planet.getCartesianFromMouseTerrain(); if (this.grabbedPoint) { this._eye0.copy(this.renderer.activeCamera.eye); this.grabbedSpheroid.radius = this.grabbedPoint.length(); this.stopRotation(); } } } stopRotation() { this.qRot.clear(); this.planet.layerLock.free(this._keyLock); this.planet.terrainLock.free(this._keyLock); this.planet._normalMapCreator.free(this._keyLock); } onMouseLeftButtonUp(e) { this.renderer.handler.canvas.classList.remove("ogGrabbingPoiner"); if (e.x === e.prev_x && e.y === e.prev_y) { this.scaleRot = 0.0; } } onMouseLeftButtonDown(e) { if (this._active) { if (!this.grabbedPoint) { return; } this.planet.stopFlying(); if (this.renderer.events.mouseState.moving) { var cam = this.renderer.activeCamera; if (cam.slope > 0.2) { var targetPoint = new Ray(cam.eye, e.direction).hitSphere(this.grabbedSpheroid); if (targetPoint) { this.scaleRot = 1.0; this.qRot = Quat.getRotationBetweenVectors( targetPoint.normal(), this.grabbedPoint.normal() ); var rot = this.qRot; cam.eye = rot.mulVec3(cam.eye); cam._u = rot.mulVec3(cam._u); cam._r = rot.mulVec3(cam._r); cam._b = rot.mulVec3(cam._b); } } else { var p0 = this.grabbedPoint, p1 = Vec3.add(p0, cam._r), p2 = Vec3.add(p0, p0.normal()); var px = new Vec3(); if (new Ray(cam.eye, e.direction).hitPlane(p0, p1, p2, px) === Ray.INSIDE) { cam.eye = this._eye0.addA(px.subA(p0).negate()); } } } } } onMouseRightButtonClick(e) { this.stopRotation(); this.planet.stopFlying(); this.pointOnEarth = this.planet.getCartesianFromPixelTerrain(e); if (this.pointOnEarth) { this.earthUp = this.pointOnEarth.normal(); } } onMouseRightButtonDown(e) { var cam = this.renderer.activeCamera; if (this.pointOnEarth && this.renderer.events.mouseState.moving) { this.renderer.controlsBag.scaleRot = 1.0; var l = (0.5 / cam.eye.distance(this.pointOnEarth)) * (cam._lonLat.height < 5.0 ? 5.0 : cam._lonLat.height) * math.RADIANS; if (l > 0.007) l = 0.007; cam.rotateHorizontal(l * (e.x - e.prev_x), false, this.pointOnEarth, this.earthUp); cam.rotateVertical(l * (e.y - e.prev_y), this.pointOnEarth, this.minSlope); } } onShiftFree() { this._shiftBusy = false; } onMouseMove(e) { if (this._active && this.renderer.events.isKeyPressed(input.KEY_ALT)) { if (!this._shiftBusy) { this._shiftBusy = true; this.onMouseRightButtonClick(e); } this.onMouseRightButtonDown(e); } } onDraw(e) { if (this._active) { var r = this.renderer; var cam = r.activeCamera; var prevEye = cam.eye.clone(); if (this.stepIndex) { r.controlsBag.scaleRot = 1.0; var sf = this.stepsForward[this.stepsCount - this.stepIndex--]; let maxAlt = cam.maxAltitude + this.planet.ellipsoid._b; let minAlt = cam.minAltitude + this.planet.ellipsoid._b; const camAlt = sf.eye.length(); if (camAlt > maxAlt || camAlt < minAlt && this._wheelDirection > 0) { this._wheelDirection = +1; return; } cam.eye = sf.eye; cam._u = sf.v; cam._r = sf.u; cam._b = sf.n; } else { if (this._deactivate) { this._deactivate = false; this.planet.layerLock.free(this._keyLock); this.planet.terrainLock.free(this._keyLock); this.planet._normalMapCreator.free(this._keyLock); } } if (r.events.mouseState.leftButtonDown || !this.scaleRot) { return; } this.scaleRot -= this.inertia; if (this.scaleRot <= 0.0) { this.scaleRot = 0.0; } else { r.controlsBag.scaleRot = this.scaleRot; var rot = this.qRot .slerp(Quat.IDENTITY, 1.0 - this.scaleRot * this.scaleRot * this.scaleRot) .normalize(); if (!(rot.x || rot.y || rot.z)) { this.scaleRot = 0.0; } cam.eye = rot.mulVec3(cam.eye); cam._u = rot.mulVec3(cam._u); cam._r = rot.mulVec3(cam._r); cam._b = rot.mulVec3(cam._b); } if (cam.eye.distance(prevEye) / cam._terrainAltitude > 0.01) { this.planet.layerLock.lock(this._keyLock); this.planet.terrainLock.lock(this._keyLock); this.planet._normalMapCreator.lock(this._keyLock); } else { this.planet.layerLock.free(this._keyLock); this.planet.terrainLock.free(this._keyLock); this.planet._normalMapCreator.free(this._keyLock); } } } } export { MouseNavigation };