mirror of
https://github.com/openglobus/openglobus.git
synced 2025-12-08 19:25:27 +00:00
437 lines
15 KiB
JavaScript
437 lines
15 KiB
JavaScript
"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 };
|