@spearwolf/twopoint5d
Version:
Create 2.5D realtime graphics and pixelart with WebGL and three.js
150 lines • 4.92 kB
JavaScript
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