@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
232 lines (181 loc) • 5.65 kB
JavaScript
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);
}
}
}