@tolokoban/tgd
Version:
ToloGameDev library for WebGL2
268 lines • 25.3 kB
JavaScript
import { tgdActionCreateCameraInterpolation, tgdEasingFunctionOutQuad, } from "./../../utils/index.js";
import { tgdCalcClamp } from "./../../utils/math.js";
import { TgdEvent } from "./../../event/index.js";
import { TgdMat3, TgdQuat, TgdVec3 } from "./../../math/index.js";
export class TgdControllerCameraOrbit {
constructor(context, { geo, minZoom = 1e-3, maxZoom = Infinity, speedZoom = 1, speedOrbit = 1, speedPanning = 1, inertiaZoom = 0, inertiaOrbit = 0, inertiaPanning = 0, fixedTarget = false, onZoomRequest = alwaysTrue, } = {}) {
this.context = context;
this.id = `TgdControllerCameraOrbit-${TgdControllerCameraOrbit.counter++}`;
this.eventChange = new TgdEvent();
this.minZoom = 1e-3;
this.maxZoom = Infinity;
this.speedZoom = 1;
this.speedOrbit = 1;
this.speedPanning = 1;
this.inertiaZoom = 0;
this.inertiaOrbit = 0;
this.inertiaPanning = 0;
/**
* If `true`, pannig will only act on `camera.shift`,
* not on `camera.target`.
*/
this.fixedTarget = false;
/**
* The camera will only move if `enabled === true`.
*/
this._enabled = true;
this.animOrbit = null;
/**
* It can be usefull to disable to orbit controller for some time
* because an animation is going on on the camera, for instance.
*/
this.disabledUntil = 0;
this.tmpQuat = new TgdQuat();
this.handleMove = (event) => {
if (!this.enabled || this.animOrbit)
return;
this.actualMove(event);
};
this.actualMove = (event) => {
const dt = event.current.t - event.previous.t;
if (dt <= 0)
return;
const { context } = this;
const { keyboard } = context.inputs;
if (event.altKey || event.current.fingersCount === 2)
return this.handlePan(event);
if (this.geo) {
const speed = keyboard.isDown("Shift") ? 0.2 : 2;
const lngDelta = keyboard.isDown("x")
? 0
: speed * (event.previous.x - event.current.x);
const latDelta = keyboard.isDown("y")
? 0
: speed * (event.previous.y - event.current.y);
const lng = this.geo.lng + lngDelta;
const lat = this.geo.lat + latDelta;
this.orbitGeo(lat, lng);
return;
}
if (keyboard.isDown("z"))
return this.handleRotateAroundZ(event);
this.orbit(event.current.x - event.previous.x, event.current.y - event.previous.y, event.shiftKey);
};
this.handleMoveStart = () => {
if (!this.enabled)
return;
const { animOrbit, context } = this;
if (animOrbit) {
context.animCancel(animOrbit);
this.animOrbit = null;
}
};
this.handleMoveEnd = (event) => {
if (!this.enabled)
return;
const { context, inertiaOrbit } = this;
if (inertiaOrbit > 0) {
const inverseDeltaTime = 1 / (event.current.t - event.previous.t);
const speedX = inverseDeltaTime * (event.current.x - event.previous.x);
const speedY = inverseDeltaTime * (event.current.y - event.previous.y);
const currentEvent = structuredClone(event);
currentEvent.current.t = Date.now();
this.animOrbit = {
duration: inertiaOrbit * 1e-3,
action: alpha => {
currentEvent.previous.t = currentEvent.current.t;
currentEvent.previous.x = currentEvent.current.x;
currentEvent.previous.y = currentEvent.current.y;
currentEvent.previous.fingersCount =
currentEvent.current.fingersCount;
currentEvent.current.t = Date.now();
const deltaTime = currentEvent.current.t - currentEvent.previous.t;
const strength = 1 - alpha;
const factor = strength * deltaTime;
currentEvent.current.x += factor * speedX;
currentEvent.current.y += factor * speedY;
this.actualMove(currentEvent);
},
easingFunction: tgdEasingFunctionOutQuad,
};
context.animSchedule(this.animOrbit);
}
};
this.handleZoom = (event) => {
if (!this.enabled ||
this.speedZoom === 0 ||
!this.onZoomRequest({
altKey: event.altKey,
ctrlKey: event.ctrlKey,
metaKey: event.metaKey,
shiftKey: event.shiftKey,
x: event.current.x,
y: event.current.y,
}))
return;
const { context } = this;
const { camera } = context;
let speed = 0.1 * this.speedZoom;
if (this.context.inputs.keyboard.isDown("Shift"))
speed *= 0.1;
const dz = -event.direction * speed;
camera.zoom = tgdCalcClamp(camera.zoom * (1 + dz), this.minZoom, this.maxZoom);
event.preventDefault();
this.fireZoomChange();
};
this.geo = undefined;
if (geo) {
this.geo = Object.assign({ lat: 0, lng: 0, minLat: -Math.PI / 2, maxLat: +Math.PI / 2, minLng: -Number.MAX_VALUE, maxLng: +Number.MAX_VALUE }, geo);
}
this.cameraInitialState = context.camera.getCurrentState();
const { inputs } = context;
inputs.pointer.eventMoveStart.addListener(this.handleMoveStart);
inputs.pointer.eventMoveEnd.addListener(this.handleMoveEnd);
inputs.pointer.eventMove.addListener(this.handleMove);
inputs.pointer.eventZoom.addListener(this.handleZoom);
this.speedOrbit = speedOrbit;
this.speedZoom = speedZoom;
this.speedPanning = speedPanning;
this.inertiaOrbit = inertiaOrbit;
this.inertiaZoom = inertiaZoom;
this.inertiaPanning = inertiaPanning;
this.fixedTarget = fixedTarget;
this.minZoom = minZoom;
this.maxZoom = maxZoom;
this.onZoomRequest = onZoomRequest;
if (this.geo)
this.orbitGeo(this.geo.lat, this.geo.lng);
globalThis.setTimeout(() => context.paint());
}
get enabled() {
return this.context.time > this.disabledUntil && this._enabled;
}
set enabled(value) {
this._enabled = value;
}
reset(animDuration, easingFunction) {
const { context } = this;
this.disableForSomeTime(animDuration);
context.animSchedule({
action: tgdActionCreateCameraInterpolation(context.camera, this.cameraInitialState),
duration: animDuration,
easingFunction,
});
}
disableForSomeTime(delayInMsec) {
this.disabledUntil = Math.max(this.disabledUntil, this.context.time + delayInMsec);
}
detach() {
const { inputs } = this.context;
inputs.pointer.eventMove.removeListener(this.handleMove);
inputs.pointer.eventZoom.removeListener(this.handleZoom);
}
orbit(deltaX, deltaY, slowDown) {
const { context } = this;
const { camera } = context;
const { keyboard } = context.inputs;
const speed = 3 * (slowDown ? 0.1 : 1) * this.speedOrbit;
const dx = deltaX * speed;
const dy = deltaY * speed;
if (!keyboard.isDown("x"))
camera.transfo.orbitAroundY(dx);
if (!keyboard.isDown("y"))
camera.transfo.orbitAroundX(-dy);
this.fireOrbitChange();
}
/**
* Set the camera orientation from latitude/longitude
* @param lat Expressed in radians
* @param lng Expressed in radians
*/
orbitGeo(lat, lng) {
const { geo } = this;
if (!geo)
return;
lat = tgdCalcClamp(lat, geo.minLat, geo.maxLat);
geo.lat = lat;
lng = tgdCalcClamp(lng, geo.minLng, geo.maxLng);
geo.lng = lng;
const { orientation } = this.cameraInitialState;
const vecZ = makeGeoVec3(lat, lng);
const vecY = makeGeoVec3(lat + Math.PI / 2, lng);
const vecX = new TgdVec3(vecY).cross(vecZ);
const mat = new TgdMat3();
orientation.toMatrix(mat);
const final = new TgdMat3(vecX, vecY, vecZ);
final.multiply(mat);
this.tmpQuat.fromMatrix(final);
this.context.camera.transfo.orientation = this.tmpQuat;
this.fireOrbitChange();
}
handlePan(event) {
const { fixedTarget, speedPanning, context } = this;
const { camera } = context;
const inverseZoom = 1 / camera.zoom;
const panSpeed = 0.5 * speedPanning * inverseZoom;
const dx = (event.current.x - event.previous.x) *
panSpeed *
camera.spaceWidthAtTarget;
const dy = (event.current.y - event.previous.y) *
panSpeed *
camera.spaceHeightAtTarget;
if (fixedTarget) {
// camera.moveShift(-dx, -dy, 0)
}
else {
camera.transfo.moveAlongAxes(-dx, -dy, 0);
}
this.fireOrbitChange();
return;
}
handleRotateAroundZ(event) {
const { camera } = this.context;
const x1 = event.previous.x;
const y1 = event.previous.y;
if (Math.abs(x1) + Math.abs(y1) === 0)
return;
const x2 = event.current.x;
const y2 = event.current.y;
if (Math.abs(x2) + Math.abs(y2) === 0)
return;
const x = x1 * x2 + y1 * y2;
const y = x1 * y2 - y1 * x2;
const ang = Math.atan2(y, x) * this.speedOrbit;
camera.transfo.orbitAroundZ(ang);
this.fireOrbitChange();
return;
}
fireOrbitChange() {
this.context.paint();
this.eventChange.dispatch(this.context.camera);
}
fireZoomChange() {
this.context.paint();
}
}
TgdControllerCameraOrbit.counter = 0;
/**
* Default function for `onZoomRequest`.
*/
const alwaysTrue = () => true;
function makeGeoVec3(lat, lng) {
const radius = Math.cos(lat);
const y = Math.sin(lat);
const z = radius * Math.cos(lng);
const x = radius * Math.sin(lng);
return new TgdVec3(x, y, z);
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3JiaXQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29udHJvbGxlci9jYW1lcmEvb3JiaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUNILGtDQUFrQyxFQUNsQyx3QkFBd0IsR0FDM0IsTUFBTSxZQUFZLENBQUE7QUFDbkIsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBTTlDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFFckMsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBMkVyRCxNQUFNLE9BQU8sd0JBQXdCO0lBdURqQyxZQUNxQixPQUFtQixFQUNwQyxFQUNJLEdBQUcsRUFDSCxPQUFPLEdBQUcsSUFBSSxFQUNkLE9BQU8sR0FBRyxRQUFRLEVBQ2xCLFNBQVMsR0FBRyxDQUFDLEVBQ2IsVUFBVSxHQUFHLENBQUMsRUFDZCxZQUFZLEdBQUcsQ0FBQyxFQUNoQixXQUFXLEdBQUcsQ0FBQyxFQUNmLFlBQVksR0FBRyxDQUFDLEVBQ2hCLGNBQWMsR0FBRyxDQUFDLEVBQ2xCLFdBQVcsR0FBRyxLQUFLLEVBQ25CLGFBQWEsR0FBRyxVQUFVLE1BQ2dCLEVBQUU7UUFiL0IsWUFBTyxHQUFQLE9BQU8sQ0FBWTtRQXJEeEIsT0FBRSxHQUFHLDRCQUE0Qix3QkFBd0IsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFBO1FBQ3JFLGdCQUFXLEdBQUcsSUFBSSxRQUFRLEVBQWEsQ0FBQTtRQUNoRCxZQUFPLEdBQUcsSUFBSSxDQUFBO1FBQ2QsWUFBTyxHQUFHLFFBQVEsQ0FBQTtRQUNsQixjQUFTLEdBQUcsQ0FBQyxDQUFBO1FBQ2IsZUFBVSxHQUFHLENBQUMsQ0FBQTtRQUNkLGlCQUFZLEdBQUcsQ0FBQyxDQUFBO1FBQ2hCLGdCQUFXLEdBQUcsQ0FBQyxDQUFBO1FBQ2YsaUJBQVksR0FBRyxDQUFDLENBQUE7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