@spearwolf/twopoint5d
Version:
a library to create 2.5d realtime graphics and pixelart with three.js
159 lines • 5.81 kB
JavaScript
import { on } from '@spearwolf/eventize';
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { Pass } from 'three/addons/postprocessing/Pass.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { StageAdded, StageRemoved, } from '../events.js';
import { hasGetRenderPass } from './IGetRenderPass.js';
import { StageRenderer } from './StageRenderer.js';
export class PostProcessingRenderer extends StageRenderer {
#orderedPasses;
constructor() {
super();
this.name = 'PostProcessingRenderer';
this.passes = [];
this.passesNeedsUpdate = true;
this.stagePass = new WeakMap();
this.passesByName = new Map();
on(this, [StageAdded, StageRemoved], this);
}
renderFrame(renderer, now, deltaTime, frameNo) {
this.stages.forEach((stage) => {
this.resizeStage(stage, this.width, this.height);
stage.stage.renderFrame(renderer, now, deltaTime, frameNo, (scene, camera, autoClear) => {
const renderPass = this.stagePass.get(stage.stage);
if (renderPass) {
renderPass.clear = autoClear;
renderPass.scene = scene;
renderPass.camera = camera;
}
});
});
const composer = this.getComposer(renderer);
if (this.passesNeedsUpdate) {
composer.passes.length = 0;
composer.passes.push(...this.getOrderedPasses());
this.passesNeedsUpdate = false;
}
composer.render();
}
onRenderOrderChanged() {
this.#orderedPasses = undefined;
}
getOrderedPasses() {
if (this.#orderedPasses) {
return this.#orderedPasses;
}
this.passesNeedsUpdate = true;
const renderOrder = this.renderOrderArray;
if (renderOrder.length === 0 || (renderOrder.length === 1 && (renderOrder[0] === '' || renderOrder[0] === '*'))) {
this.#orderedPasses = this.passes;
return this.passes;
}
const otherNames = new Set(Array.from(this.passesByName.keys()));
renderOrder.forEach((name) => {
if (name !== '*') {
otherNames.delete(name);
}
});
const orderedPasses = renderOrder
.map((name) => {
if (name === '*') {
return Array.from(otherNames)
.map((name) => Array.from(this.passesByName.get(name)))
.flat();
}
if (this.passesByName.has(name)) {
return Array.from(this.passesByName.get(name));
}
})
.flat()
.filter(Boolean);
this.#orderedPasses = orderedPasses;
return orderedPasses;
}
addPass(pass, name) {
this.passes.push(pass);
this.composer?.addPass(pass);
name = name || '*';
if (this.passesByName.has(name)) {
this.passesByName.get(name).add(pass);
}
else {
this.passesByName.set(name, new Set([pass]));
}
this.#orderedPasses = undefined;
}
removePass(pass) {
this.composer?.removePass(pass);
const index = this.passes.indexOf(pass);
if (index !== -1) {
this.passes.splice(index, 1);
}
for (const [, passes] of this.passesByName) {
if (passes.has(pass)) {
passes.delete(pass);
}
}
this.#orderedPasses = undefined;
}
getComposer(renderer) {
if (this.composer == null) {
if (renderer.isWebGPURenderer) {
throw new Error('PostProcessingRenderer: WebGPURenderer not supported');
}
this.composer = new EffectComposer(renderer);
this.onResizeRenderer(this.width, this.height, this.pixelRatio);
this.passes.forEach((pass) => this.composer.addPass(pass));
}
return this.composer;
}
onResizeRenderer(width, height, pixelRatio) {
if (this.composer) {
this.composer.setPixelRatio(pixelRatio);
this.composer.setSize(width, height);
}
}
stageAdded({ stage }) {
if (hasGetRenderPass(stage) && !this.stagePass.has(stage)) {
const renderPass = stage.getRenderPass();
this.stagePass.set(stage, renderPass);
this.addPass(renderPass, stage.name);
}
}
stageRemoved({ stage }) {
if (this.stagePass.has(stage)) {
const renderPass = this.stagePass.get(stage);
this.stagePass.delete(stage);
this.removePass(renderPass);
renderPass.dispose();
}
}
reorderPasses(passes) {
const nextPasses = passes
.map((pass) => {
if (pass instanceof Pass) {
return pass;
}
return this.stagePass.get(pass);
})
.filter(Boolean);
const target = this.passes;
if (target.length !== nextPasses.length) {
console.error('[PostProcessingRenderer] reorderPasses: nextPasses length mismatch: should be', target.length, 'but is', nextPasses.length, { target, passes, nextPasses, renderer: this });
}
target.length = 0;
target.push(...nextPasses);
}
dispose() {
this.passes.forEach((pass) => pass.dispose());
this.passes.length = 0;
if (this.composer) {
this.composer.dispose();
this.composer = undefined;
}
this.passesByName.clear();
this.passesNeedsUpdate = true;
this.#orderedPasses = undefined;
}
}
//# sourceMappingURL=PostProcessingRenderer.js.map