UNPKG

@spearwolf/twopoint5d

Version:

Create 2.5D realtime graphics and pixelart with WebGL and three.js

150 lines 4.92 kB
import { emit, eventize, retain } from '@spearwolf/eventize'; import { pass } from 'three/tsl'; import { Scene } from 'three/webgpu'; import { OnStageAfterCameraChanged, OnStageFirstFrame, OnStageResize, OnStageUpdateFrame, } from '../events.js'; export class Stage2D { get name() { return this.scene.name; } set name(name) { this.scene.name = name; } #containerWidth; #containerHeight; get containerWidth() { return this.#containerWidth; } get containerHeight() { return this.#containerHeight; } #width; #height; get width() { return this.#width; } get height() { return this.#height; } #projection; get projection() { return this.#projection; } set projection(projection) { if (this.#projection !== projection) { this.#projection = projection; this.#cameraFromProjection = undefined; this.updateProjection(true); } } #cameraFromProjection; #cameraUserOverride; get camera() { return this.#cameraUserOverride ?? this.#cameraFromProjection; } set camera(camera) { this.#updateCamera(() => void (this.#cameraUserOverride = camera)); } #updateCamera; constructor(projection, scene) { this.isStage2D = true; this.needsUpdate = false; this.#containerWidth = 0; this.#containerHeight = 0; this.#width = 0; this.#height = 0; this.#updateCamera = (updateCallback) => { const prevCamera = this.camera; updateCallback(); if (prevCamera !== this.camera) { const args = [this, prevCamera]; emit(this, OnStageAfterCameraChanged, ...args); } }; this.#updateProjection = (width, height) => { this.needsUpdate = false; this.projection.updateViewRect(width, height); const [w, h] = this.projection.getViewRect(); const prevWidth = this.#width; const prevHeight = this.#height; this.#width = w; this.#height = h; if (this.camera != null) { this.projection.updateCamera(this.camera); } else { this.#updateCamera(() => { this.#cameraFromProjection = this.projection.createCamera(); }); } if (prevWidth !== w || prevHeight !== h) { const props = { stage: this, width: w, height: h, }; emit(this, OnStageResize, props); } }; this.isFirstFrame = true; this.#noCameraErrorCount = 0; eventize(this); retain(this, OnStageFirstFrame); this.projection = projection; if (scene) { this.scene = scene; } else { this.scene = new Scene(); this.scene.name = 'Stage2D'; } } resize(containerWidth, containerHeight) { if (containerWidth !== this.#containerWidth || containerHeight !== this.#containerHeight) { this.#containerWidth = containerWidth; this.#containerHeight = containerHeight; if (this.projection) { this.#updateProjection(containerWidth, containerHeight); } } } updateProjection(forceUpdate = false) { if ((forceUpdate || this.needsUpdate) && this.projection) { this.#updateProjection(this.#containerWidth, this.#containerHeight); } } #updateProjection; #noCameraErrorCount; updateFrame(now, deltaTime, frameNo) { const { scene, camera } = this; if (scene == null || camera == null) { if (!camera && ++this.#noCameraErrorCount === 100) { this.#noCameraErrorCount = -1000; console.warn('Stage2D has no camera and therefore cannot be rendered! normally this only happens if you forget to call the resize() method ..'); } return; } const updateFrameProps = { stage: this, now, deltaTime, frameNo, }; if (this.isFirstFrame) { emit(this, OnStageFirstFrame, updateFrameProps); this.isFirstFrame = false; } emit(this, OnStageUpdateFrame, updateFrameProps); } renderTo(renderer) { if (this.scene && this.camera) { renderer.render(this.scene, this.camera); } } asPassNode(_renderer) { if (!this.scene || !this.camera) { throw new Error('Stage2D.asPassNode(): no scene or camera yet — call resize() first'); } return pass(this.scene, this.camera); } } //# sourceMappingURL=Stage2D.js.map