'use strict';
const WebGLContext = require('./WebGLContext');
const TEXTURE_TARGETS = {
TEXTURE_2D: true,
TEXTURE_CUBE_MAP: true
};
const DEPTH_FORMATS = {
DEPTH_COMPONENT: true,
DEPTH_STENCIL: true
};
/**
* A render target class to allow rendering to textures.
*/
class RenderTarget {
/**
* Instantiates a RenderTarget object.
*/
constructor() {
this.gl = WebGLContext.get();
this.framebuffer = this.gl.createFramebuffer();
this.textures = new Map();
}
/**
* Binds the renderTarget object.
*
* @returns {RenderTarget} The renderTarget object, for chaining.
*/
bind() {
// bind framebuffer
const gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffer);
return this;
}
/**
* Unbinds the renderTarget object.
*
* @returns {RenderTarget} The renderTarget object, for chaining.
*/
unbind() {
// unbind framebuffer
const gl = this.gl;
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
return this;
}
/**
* Attaches the provided texture to the provided attachment location.
*
* @param {Texture2D} texture - The texture to attach.
* @param {number} index - The attachment index. (optional)
* @param {string} target - The texture target type. (optional)
*
* @returns {RenderTarget} The renderTarget object, for chaining.
*/
setColorTarget(texture, index, target) {
const gl = this.gl;
if (!texture) {
throw 'Texture argument is missing';
}
if (TEXTURE_TARGETS[index] && target === undefined) {
target = index;
index = 0;
}
if (index === undefined) {
index = 0;
} else if (!Number.isInteger(index) || index < 0) {
throw 'Texture color attachment index is invalid';
}
if (target && !TEXTURE_TARGETS[target]) {
throw 'Texture target is invalid';
}
this.textures.set(`color_${index}`, texture);
this.bind();
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl['COLOR_ATTACHMENT' + index],
gl[target || 'TEXTURE_2D'],
texture.texture,
0);
this.unbind();
return this;
}
/**
* Attaches the provided texture to the provided attachment location.
*
* @param {Texture2D} texture - The texture to attach.
*
* @returns {RenderTarget} The renderTarget object, for chaining.
*/
setDepthTarget(texture) {
if (!texture) {
throw 'Texture argument is missing';
}
if (!DEPTH_FORMATS[texture.format]) {
throw 'Provided texture is not of format `DEPTH_COMPONENT` or `DEPTH_STENCIL`';
}
const gl = this.gl;
this.textures.set('depth', texture);
this.bind();
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.DEPTH_ATTACHMENT,
gl.TEXTURE_2D,
texture.texture,
0);
this.unbind();
return this;
}
/**
* Resizes the renderTarget and all attached textures by the provided height and width.
*
* @param {number} width - The new width of the renderTarget.
* @param {number} height - The new height of the renderTarget.
*
* @returns {RenderTarget} The renderTarget object, for chaining.
*/
resize(width, height) {
if (typeof width !== 'number' || (width <= 0)) {
throw `Provided \`width\` of ${width} is invalid`;
}
if (typeof height !== 'number' || (height <= 0)) {
throw `Provided \`height\` of ${height} is invalid`;
}
this.textures.forEach(texture => {
texture.resize(width, height);
});
return this;
}
}
module.exports = RenderTarget;