UNPKG

@tolokoban/tgd

Version:

ToloGameDev library for WebGL2

263 lines 18.9 kB
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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