UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

232 lines (181 loc) • 5.65 kB
import { assert } from "../../../../core/assert.js"; /** * * @type {FrameBuffer[]} */ const scratch_buffer_array = []; export class FrameBufferManager { constructor() { /** * * @type {FrameBuffer[]} */ this.buffers = []; /** * * @type {boolean} */ this.needsRebuild = false; /** * * @type {FrameBuffer[]} */ this.orderedBuffers = []; /** * * @type {number} */ this.pixelRatio = 1; } /** * * @param {WebGLRenderer} renderer */ initialize(renderer) { assert.notEqual(renderer, undefined, 'renderer is undefined'); assert.notEqual(renderer, null, 'renderer is null'); for (const b of this.buffers) { b.initialize(renderer); //expect render target to be set if (typeof b.renderTarget !== 'object') { throw new Error(`renderTarget was expected to be of type 'object', instead was '${typeof b.renderTarget}'`) } if (b.renderTarget.isWebGLRenderTarget !== true) { throw new Error(`renderTarget.isWebGLRenderTarget must be true, instead was '${b.renderTarget.isWebGLRenderTarget}'`); } } } build() { this.orderedBuffers = this.computeRenderOrder(); this.needsRebuild = false; } /** * * @returns {FrameBuffer[]} */ computeRenderOrder() { const openSet = this.buffers.slice(); const resolved_id_map = {}; const result = []; /** * * @param {FrameBuffer} buffer */ function dependenciesSatisfied(buffer) { const dependencies = buffer.dependencies; for (let i = 0; i < dependencies.length; i++) { const id = dependencies[i]; if (resolved_id_map[id] === undefined) { //buffer has not been assigned yet return false; } } //all dependencies satisfied return true; } let openSetCount = openSet.length; while (openSetCount > 0) { //remember number of buffers in the open set const openSetCountBefore = openSetCount; for (let i = 0; i < openSetCount; i++) { const frameBuffer = openSet[i]; if (dependenciesSatisfied(frameBuffer)) { result.push(frameBuffer); openSet.splice(i, 1); resolved_id_map[frameBuffer.id] = frameBuffer; //update iteration variables i--; openSetCount--; } } if (openSetCountBefore === openSetCount) { //no buffers allocated in this pass throw new Error(`dependencies could not be satisfied for ${openSetCount} buffers`); } } return result; } /** * * @param {string} id * @returns {FrameBuffer|undefined} */ getById(id) { assert.isString(id, 'id'); return this.buffers.find(function (buffer) { return buffer.id === id; }); } /** * * @param {FrameBuffer} buffer */ add(buffer) { const existing = this.getById(buffer.id); if (existing !== undefined) { throw new Error(`Failed to add buffer. Another buffer with id='${buffer.id}' already exists`); } this.buffers.push(buffer); //mark for rebuild this.needsRebuild = true; } update() { if (this.needsRebuild) { this.build(); } } /** * * @param {FrameBuffer[]} result * @param {FrameBuffer} dependant * @private */ __populate_dependencies(result, dependant) { const dependency_ids = dependant.dependencies; const dependency_count = dependency_ids.length; for (let i = 0; i < dependency_count; i++) { const dependencyId = dependency_ids[i]; const dependency = this.getById(dependencyId); result[i] = dependency; } } /** * * @param {WebGLRenderer} renderer * @param {Camera} camera * @param {Scene} scene */ render(renderer, camera, scene) { this.update(); const buffers = this.orderedBuffers; const numBuffers = buffers.length; for (let i = 0; i < numBuffers; i++) { const buffer = buffers[i]; if (buffer.referenceCount > 0) { this.__populate_dependencies(scratch_buffer_array, buffer); buffer.render(renderer, camera, scene, scratch_buffer_array); } } } /** * * @param {number} value */ setPixelRatio(value) { this.pixelRatio = value; } /** * * @param {number} x * @param {number} y */ setSize(x, y) { const buffers = this.buffers; const numBuffers = buffers.length; const pixelRatio = this.pixelRatio; for (let i = 0; i < numBuffers; i++) { const buffer = buffers[i]; buffer.setSize(x * pixelRatio, y * pixelRatio); } } }