@tolokoban/tgd
Version:
ToloGameDev library for WebGL2
263 lines • 18.9 kB
JavaScript
import { TgdMat4, TgdTransfo, TgdVec4 } from "./../math/index.js";
const lastText = "";
export class TgdCamera {
constructor(options = {}) {
this._screenWidth = 1920;
this._screenHeight = 1080;
this._screenAspectRatio = 1920 / 1080;
/** Do we need recalculation? */
this._dirtyModelView = true;
this.dirtyModelViewInverse = true;
this._dirtyAxis = true;
this._dirtyProjection = true;
this.dirtyProjectionInverse = true;
this._near = 1e-3;
this._far = Number.POSITIVE_INFINITY;
// transformation
this._matrixModelView = new TgdMat4();
this._matrixProjectionInverse = new TgdMat4();
this._zoom = 1;
this.name = options.name ?? `TgdCamera#${TgdCamera.incrementalId++}`;
this._near = options.near ?? 1e-3;
this._far = options.far ?? 1e6;
this.transfo = new TgdTransfo(options.transfo);
this.zoom = options.zoom ?? 1;
}
getCurrentState() {
return {
distance: this.transfo.distance,
orientation: this.transfo.orientation.clone(),
spaceHeightAtTarget: this.spaceHeightAtTarget,
position: this.transfo.position.clone(),
zoom: this.zoom,
};
}
setCurrentState(state) {
this.transfo.distance = state.distance;
this.transfo.orientation = state.orientation.clone();
this.spaceHeightAtTarget = state.spaceHeightAtTarget;
this.transfo.position = state.position.clone();
this.zoom = state.zoom;
}
get near() {
return this._near;
}
set near(v) {
if (v === this._near)
return;
this._near = v;
this.dirtyProjection = true;
}
get far() {
return this._far;
}
set far(v) {
if (v === this._far)
return;
this._far = v;
this.dirtyProjection = true;
}
/**
* Size in space of a screen pixel at target.
* Useful for scalebars (mostly in Orthographic mode).
*/
get spacePerPixel() {
return 2 * this.spaceHeightAtTarget / (this.screenHeight * this.zoom);
}
get screenAspectRatio() {
return this._screenAspectRatio;
}
get screenWidth() {
return this._screenWidth;
}
set screenWidth(v) {
if (v === this._screenWidth)
return;
this._screenWidth = v;
this.dirtyProjection = true;
this._screenAspectRatio = this._screenWidth / this._screenHeight;
}
get screenHeight() {
return this._screenHeight;
}
set screenHeight(v) {
if (v === this._screenHeight)
return;
this._screenHeight = v;
this.dirtyProjection = true;
this._screenAspectRatio = this._screenWidth / this._screenHeight;
}
get spaceHeightAtTarget() {
return this.getSpaceHeightAtTarget();
}
set spaceHeightAtTarget(v) {
this.setSpaceHeightAtTarget(v);
}
get spaceWidthAtTarget() {
return (this.screenWidth * this.spaceHeightAtTarget) / this.screenHeight;
}
set spaceWidthAtTarget(v) {
this.setSpaceHeightAtTarget((v * this.screenHeight) / this.screenWidth);
}
/**
* Adapt the camera position so that a rectangle of `width` and `height`,
* centered at the camera target, can be the biggest without being cut.
* @param width width in space units.
* @param height height in space units.
*/
fitSpaceAtTarget(width, height, cover = false) {
const ratio = width / height;
if (cover) {
if (this._screenAspectRatio < ratio) {
this.spaceHeightAtTarget = height;
}
else {
this.spaceWidthAtTarget = width;
}
}
else {
if (this._screenAspectRatio > ratio) {
this.spaceHeightAtTarget = height;
}
else {
this.spaceWidthAtTarget = width;
}
}
}
from(camera) {
const { zoom, screenWidth, screenHeight } = camera;
this.transfo.from(camera.transfo);
this.zoom = zoom;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.dirtyModelView = true;
this.copyProjectionFrom(camera);
return this;
}
fromTransfo(transfo) {
this.transfo.from(transfo);
this.dirtyModelView = true;
return this;
}
apply(point) {
return new TgdVec4(point).applyMatrix(this.matrixModelView).applyMatrix(this.matrixProjection);
}
/**
* Useful for LOD. If an object takes a small space on the screen,
* we can display its low res version.
* @returns The surface of the bounding square in screen space of a space bbox.
* Or 0 if the bounding box is out of the frustrum.
*/
computeBoundingBoxVisibleSurface(bbox) {
const [x0, y0, z0] = bbox.min;
const [x1, y1, z1] = bbox.max;
/**
* We use the inside tetrahedron to only compute
* 4 points instead of 8.
*/
const points = [
[x0, y0, z0],
// [x0, y1, z0],
// [x1, y0, z0],
[x1, y1, z0],
// [x0, y0, z1],
[x0, y1, z1],
[x1, y0, z1],
// [x1, y1, z1],
];
let left = 0;
let right = 0;
let top = 0;
let bottom = 0;
let near = 0;
let far = 0;
let minX = +Number.MAX_VALUE;
let maxX = -Number.MAX_VALUE;
let minY = +Number.MAX_VALUE;
let maxY = -Number.MAX_VALUE;
const debug = [];
for (const point of points) {
const [xx, yy, zz, ww] = this.apply(point);
const x = xx / ww;
const y = yy / ww;
const z = zz / ww;
debug.push([point, x, y, z]);
maxX = Math.max(maxX, x);
minX = Math.min(minX, x);
maxY = Math.max(maxY, y);
minY = Math.min(minY, y);
if (x < -1)
left++;
else if (x > +1)
right++;
if (y < -1)
top++;
else if (y > +1)
bottom++;
if (z < 0)
near++;
else if (z > 1)
far--;
}
const N = points.length;
if (left === N || right === N || top === N || bottom === N || near === N || far === N) {
return 0;
}
return (maxX - minX) * (maxY - minY);
}
/**
* This matrix will transform a world coordinate into a camera coordinate.
*/
get matrixModelView() {
return this._matrixModelView.invert(this.transfo.matrix);
}
get matrixProjectionInverse() {
if (this.dirtyProjectionInverse) {
this._matrixProjectionInverse.invert(this.matrixProjection);
this.dirtyProjectionInverse = false;
}
return this._matrixProjectionInverse;
}
get zoom() {
return this._zoom;
}
set zoom(v) {
if (this._zoom === v)
return;
this._zoom = v;
this._dirtyProjection = true;
}
toCode(caption) {
return `// ${caption ?? "TgdCamera"}\n// Not implemented yet`;
}
debug(caption) {
const name = `${this.name}: ${caption ?? ""}`;
console.debug("TgdCamera", name);
console.debug(" Distance:", this.transfo.distance);
console.debug(" Zoom:", this.zoom);
this.transfo.orientation.debug(" Orientation");
this.transfo.position.debug(" Target");
this.transfo.actualPosition.debug(" Actual position");
this.matrixModelView.debug(" MatrixModelView");
this.matrixProjection.debug(" MatrixProjection");
}
get dirtyModelView() {
return this._dirtyModelView;
}
set dirtyModelView(v) {
this._dirtyModelView = v;
if (v) {
this.dirtyModelViewInverse = true;
}
}
get dirtyProjection() {
return this._dirtyProjection;
}
set dirtyProjection(v) {
this._dirtyProjection = v;
if (v)
this.dirtyProjectionInverse = true;
}
}
TgdCamera.incrementalId = 1;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2FtZXJhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NhbWVyYS9jYW1lcmEudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLE9BQU8sRUFBZ0IsVUFBVSxFQUF3QyxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFxQjVHLE1BQU0sUUFBUSxHQUFHLEVBQUUsQ0FBQTtBQUVuQixNQUFNLE9BQWdCLFNBQVM7SUF5QjNCLFlBQVksVUFBNEIsRUFBRTtRQW5CbEMsaUJBQVksR0FBRyxJQUFJLENBQUE7UUFDbkIsa0JBQWEsR0FBRyxJQUFJLENBQUE7UUFDcEIsdUJBQWtCLEdBQUcsSUFBSSxHQUFHLElBQUksQ0FBQTtRQUV4QyxnQ0FBZ0M7UUFDeEIsb0JBQWUsR0FBRyxJQUFJLENBQUE7UUFDdEIsMEJBQXFCLEdBQUcsSUFBSSxDQUFBO1FBQzVCLGVBQVUsR0FBRyxJQUFJLENBQUE7UUFDZixxQkFBZ0IsR0FBRyxJQUFJLENBQUE7UUFDdkIsMkJBQXNCLEdBQUcsSUFBSSxDQUFBO1FBRTdCLFVBQUssR0FBRyxJQUFJLENBQUE7UUFDWixTQUFJLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFBO1FBRXpDLGlCQUFpQjtRQUNBLHFCQUFnQixHQUFHLElBQUksT0FBTyxFQUFFLENBQUE7UUFDaEMsNkJBQXdCLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQTtRQUNqRCxVQUFLLEdBQUcsQ0FBQyxDQUFBO1FBR2IsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUMsSUFBSSxJQUFJLGFBQWEsU0FBUyxDQUFDLGFBQWEsRUFBRSxFQUFFLENBQUE7UUFDcEUsSUFBSSxDQUFDLEtBQUssR0FBRyxPQUFPLENBQUMsSUFBSSxJQUFJLElBQUksQ0FBQTtRQUNqQyxJQUFJLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLElBQUksR0FBRyxDQUFBO1FBQzlCLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxVQUFVLENB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