import {mat4Ortho, mat4LookAt, mat4Multiply} from './math.js'; export class Diorama { constructor(elementId, frame, requestRenderCallback) { this.element = document.getElementById(elementId); this.element.style.position = 'relative'; // todo move to css this.canvas = document.createElement('canvas') this.element.appendChild(this.canvas) this.ctx2d = this.canvas.getContext('2d'); this.frame = frame; this.requestRender = requestRenderCallback; this.target = [0.5, 0, 0.5]; this.radius = 4; this.theta = 135; this.phi = 30; this.projMatrix = new Float32Array(16); this.viewMatrix = new Float32Array(16); this.viewProjMatrix = new Float32Array(16); this.checkpoint = null; } saveState() { this.checkpoint = { target: [...this.target], radius: this.radius, theta: this.theta, phi: this.phi, }; } loadState() { if (!this.checkpoint) return; this.target = [...this.checkpoint.target]; this.radius = this.checkpoint.radius; this.theta = this.checkpoint.theta; this.phi = this.checkpoint.phi; this.requestRender(); } centerView() { if (this.frame.blocks.size === 0) { this.target = [0.5, 0.5, 0.5]; return; } let minX = Infinity, minY = Infinity, minZ = Infinity; let maxX = -Infinity, maxY = -Infinity, maxZ = -Infinity; for (const block of this.frame.blocks.values()) { minX = Math.min(minX, block.pos[0]); minY = Math.min(minY, block.pos[1]); minZ = Math.min(minZ, block.pos[2]); maxX = Math.max(maxX, block.pos[0] + 1); maxY = Math.max(maxY, block.pos[1] + 1); maxZ = Math.max(maxZ, block.pos[2] + 1); } this.target = [(minX + maxX) / 2, (minY + maxY) / 2, (minZ + maxZ) / 2]; this.requestRender(); } updateMatrices(aspectRatio) { const t = this.theta * Math.PI / 180; const p = this.phi * Math.PI / 180; this.viewDir = [ Math.cos(p) * Math.sin(t), Math.sin(p), Math.cos(p) * Math.cos(t) ]; this.upDir = [ -Math.sin(p) * Math.sin(t), Math.cos(p), -Math.sin(p) * Math.cos(t), ]; this.eyePos = [ this.target[0] + this.radius * this.viewDir[0], this.target[1] + this.radius * this.viewDir[1], this.target[2] + this.radius * this.viewDir[2], ]; const size = this.radius * 0.5; mat4Ortho(this.projMatrix, -size * aspectRatio, size * aspectRatio, -size, size, -50, 50); mat4LookAt( this.viewMatrix, this.eyePos[0], this.eyePos[1], this.eyePos[2], this.target[0], this.target[1], this.target[2], this.upDir[0], this.upDir[1], this.upDir[2], ); mat4Multiply(this.viewProjMatrix, this.projMatrix, this.viewMatrix); return this.viewProjMatrix; } }