UNPKG

@spearwolf/twopoint5d

Version:

a library to create 2.5d realtime graphics and pixelart with three.js

159 lines 5.81 kB
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