@tolokoban/tgd
Version:
ToloGameDev library for WebGL2
153 lines • 14.2 kB
JavaScript
import { TgdPainterGroup } from "./group.js";
import { webglCreateFramebuffer, webglLookup } from "./../utils/index.js";
export class TgdPainterFramebuffer extends TgdPainterGroup {
constructor(context, options) {
super(options.children);
this.context = context;
this.options = options;
/**
* The framebuffer becomes dirty as soon as the width or height changes.
*/
this.dirty = true;
this._width = 0;
this._height = 0;
this._framebuffer = null;
this._depthBuffer = null;
this._stencilBuffer = null;
const { textureColor0, textureColor1, textureColor2, textureColor3 } = options;
if (!(textureColor0 || textureColor1 || textureColor2 || textureColor3)) {
console.error("[TgdPainterFramebuffer] You gave no color texture in the constructor: nothing will be rendered!", options);
}
this.textureColor0 = nameTexture(textureColor0, "textureColor0");
this.textureColor1 = nameTexture(textureColor1, "textureColor1");
this.textureColor2 = nameTexture(textureColor2, "textureColor2");
this.textureColor3 = nameTexture(textureColor3, "textureColor3");
this.textureDepth = options.textureDepth;
this.onEnter = options.onEnter;
this.onExit = options.onExit;
const { gl } = this.context;
this.drawBuffers = [
this.textureColor0 ? gl.COLOR_ATTACHMENT0 : gl.NONE,
this.textureColor1 ? gl.COLOR_ATTACHMENT1 : gl.NONE,
this.textureColor2 ? gl.COLOR_ATTACHMENT2 : gl.NONE,
this.textureColor3 ? gl.COLOR_ATTACHMENT3 : gl.NONE,
];
}
get width() {
return this._width;
}
set width(v) {
if (this._width === v)
return;
this._width = v;
this.dirty = true;
}
get height() {
return this._height;
}
set height(v) {
if (this._height === v)
return;
this._height = v;
this.dirty = true;
}
updateTextureForColor(tgdTexture, attachment) {
if (!tgdTexture)
return;
const { context, width, height } = this;
const { gl } = context;
tgdTexture.resize(width, height);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + attachment, gl.TEXTURE_2D, tgdTexture.glTexture, 0);
}
createTextureForDepth() {
const tgdTexture = this.textureDepth;
if (!tgdTexture)
return;
const { context, width, height } = this;
const { gl } = context;
tgdTexture.resize(width, height);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, tgdTexture.glTexture, 0);
}
createDepthBuffer(gl) {
if (this.options.depthBuffer === false)
return;
const { width, height } = this;
// Create a Depth Buffer, because the default
// framebuffer has none.
const depthBuffer = gl.createRenderbuffer();
if (!depthBuffer)
throw new Error("Unable to create WebGLRenderBuffer for depth!");
this._depthBuffer = depthBuffer;
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer);
}
createStencilBuffer(gl) {
if (this.options.stencilBuffer !== false) {
const { width, height } = this;
const stencilBuffer = gl.createRenderbuffer();
if (!stencilBuffer)
throw new Error("Unable to create WebGLRenderBuffer for stencil!");
this._stencilBuffer = stencilBuffer;
gl.bindRenderbuffer(gl.RENDERBUFFER, stencilBuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, stencilBuffer);
}
}
createFramebufferIfNeeded() {
if (!this.dirty)
return;
const { context } = this;
const { gl } = context;
this.delete();
this._framebuffer = webglCreateFramebuffer(gl);
gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);
this.updateTextureForColor(this.textureColor0, 0);
this.updateTextureForColor(this.textureColor1, 1);
this.updateTextureForColor(this.textureColor2, 2);
this.updateTextureForColor(this.textureColor3, 3);
this.createTextureForDepth();
this.createDepthBuffer(gl);
this.createStencilBuffer(gl);
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status !== gl.FRAMEBUFFER_COMPLETE) {
console.error(`Your Framebuffer is incomplete: ${webglLookup(status)}!`);
}
this.dirty = false;
}
paint(time, delay) {
const { context, options } = this;
const { gl } = context;
const { viewportMatchingScale = 1 } = options;
this.width = Math.round(context.width * viewportMatchingScale);
this.height = Math.round(context.height * viewportMatchingScale);
this.createFramebufferIfNeeded();
gl.bindFramebuffer(gl.FRAMEBUFFER, this._framebuffer);
gl.drawBuffers(this.drawBuffers);
super.paint(time, delay);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
}
delete() {
const { context, _framebuffer, _depthBuffer, _stencilBuffer } = this;
const { gl } = context;
if (_framebuffer) {
gl.deleteFramebuffer(_framebuffer);
this._framebuffer = null;
}
if (_depthBuffer) {
gl.deleteRenderbuffer(_depthBuffer);
this._depthBuffer = null;
}
if (_stencilBuffer) {
gl.deleteRenderbuffer(_stencilBuffer);
this._stencilBuffer = null;
}
}
}
function nameTexture(texture, name) {
if (texture) {
texture.name += `/${name}`;
}
return texture;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZnJhbWVidWZmZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvcGFpbnRlci9mcmFtZWJ1ZmZlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ3pDLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxXQUFXLEVBQUUsTUFBTSxZQUFZLENBQUE7QUEwQ2hFLE1BQU0sT0FBTyxxQkFBc0IsU0FBUSxlQUFlO0lBa0J0RCxZQUNxQixPQUFtQixFQUNuQixPQUE4QztRQUUvRCxLQUFLLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO1FBSE4sWUFBTyxHQUFQLE9BQU8sQ0FBWTtRQUNuQixZQUFPLEdBQVAsT0FBTyxDQUF1QztRQWJuRTs7V0FFRztRQUNLLFVBQUssR0FBRyxJQUFJLENBQUE7UUFDWixXQUFNLEdBQUcsQ0FBQyxDQUFBO1FBQ1YsWUFBTyxHQUFHLENBQUMsQ0FBQTtRQUNYLGlCQUFZLEdBQTRCLElBQUksQ0FBQTtRQUM1QyxpQkFBWSxHQUE2QixJQUFJLENBQUE7UUFDN0MsbUJBQWMsR0FBNkIsSUFBSSxDQUFBO1FBUW5ELE1BQU0sRUFBRSxhQUFhLEVBQUUsYUFBYSxFQUFFLGFBQWEsRUFBRSxhQUFhLEVBQUUsR0FDaEUsT0FBTyxDQUFBO1FBQ1gsSUFDSSxDQUFDLENBQUMsYUFBYSxJQUFJLGFBQWEsSUFBSSxhQUFhLElBQUksYUFBYSxDQUFDLEVBQ3JFLENBQUM7WUFDQyxPQUFPLENBQUMsS0FBSyxDQUNULGlHQUFpRyxFQUNqRyxPQUFPLENBQ1YsQ0FBQTtRQUNMLENBQUM7UUFDRCxJQUFJLENBQUMsYUFBYSxHQUFHLFdBQVcsQ0FBQyxhQUFhLEVBQUUsZUFBZSxDQUFDLENBQUE7UUFDaEUsSUFBSSxDQUFDLGFBQWEsR0FBRyxXQUFXLENBQUMsYUFBYSxFQUFFLGVBQWUsQ0FBQyxDQUFBO1FBQ2hFLElBQUksQ0FBQyxhQUFhLEdBQUcsV0FB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