cables_dev/cables/src/libs/cgl/cubemapframebuffer/cubemapframebuffer.js
import { CubemapTexture } from "./cubemaptexture.js";
class CubemapFramebuffer
{
constructor(cgl, width, height, options)
{
this._cgl = cgl;
this.width = width || 8;
this.height = height || 8;
this._cubemapProperties = [
// targets for use in some gl functions for working with cubemaps
{
"face": this._cgl.gl.TEXTURE_CUBE_MAP_POSITIVE_X,
"lookAt": vec3.fromValues(1.0, 0.0, 0.0),
"up": vec3.fromValues(0.0, -1.0, 0.0),
},
{
"face": this._cgl.gl.TEXTURE_CUBE_MAP_NEGATIVE_X,
"lookAt": vec3.fromValues(-1.0, 0.0, 0.0),
"up": vec3.fromValues(0.0, -1.0, 0.0),
},
{
"face": this._cgl.gl.TEXTURE_CUBE_MAP_POSITIVE_Y,
"lookAt": vec3.fromValues(0.0, 1.0, 0.0),
"up": vec3.fromValues(0.0, 0.0, 1.0),
},
{
"face": this._cgl.gl.TEXTURE_CUBE_MAP_NEGATIVE_Y,
"lookAt": vec3.fromValues(0.0, -1.0, 0.0),
"up": vec3.fromValues(0.0, 0.0, -1.0),
},
{
"face": this._cgl.gl.TEXTURE_CUBE_MAP_POSITIVE_Z,
"lookAt": vec3.fromValues(0.0, 0.0, 1.0),
"up": vec3.fromValues(0.0, -1.0, 0.0),
},
{
"face": this._cgl.gl.TEXTURE_CUBE_MAP_NEGATIVE_Z,
"lookAt": vec3.fromValues(0.0, 0.0, -1.0),
"up": vec3.fromValues(0.0, -1.0, 0.0),
},
];
this._lookAtTemp = vec3.fromValues(0, 0, 0);
this.camPos = vec3.fromValues(0, 0, 0);
this._modelMatrix = mat4.create();
this._viewMatrix = mat4.create();
this._projectionMatrix = mat4.perspective(mat4.create(), CGL.DEG2RAD * 90, 1, 0.1, 1000.0);
this._depthRenderbuffer = null;
this._framebuffer = null;
this._depthbuffer = null;
// this._textureFrameBuffer = null;
this._textureDepth = null;
this._options = options || {
// "isFloatingPointTexture": false
};
this.name = this._options.name || "unknown cubemapframebuffer";
if (!this._options.hasOwnProperty("numRenderBuffers")) this._options.numRenderBuffers = 1;
if (!this._options.hasOwnProperty("depth")) this._options.depth = true;
if (!this._options.hasOwnProperty("clear")) this._options.clear = true;
if (!this._options.hasOwnProperty("multisampling"))
{
this._options.multisampling = false;
this._options.multisamplingSamples = 0;
}
if (this._options.multisamplingSamples)
{
if (this._cgl.glSlowRenderer) this._options.multisamplingSamples = 0;
if (!this._cgl.gl.MAX_SAMPLES) this._options.multisamplingSamples = 0;
else this._options.multisamplingSamples = Math.min(this._cgl.gl.getParameter(this._cgl.gl.MAX_SAMPLES), this._options.multisamplingSamples);
}
if (!this._options.hasOwnProperty("filter")) this._options.filter = CGL.Texture.FILTER_LINEAR;
if (!this._options.hasOwnProperty("wrap")) this._options.wrap = CGL.Texture.WRAP_CLAMP_TO_EDGE;
this._cgl.checkFrameStarted("cubemap framebuffer");
let pxlFormat = options.pixeFormat;
if (!pxlFormat && options.isFloatingPointTexture)pxlFormat = CGL.Texture.PFORMATSTR_RGBA32F;
this.texture = new CubemapTexture(this._cgl, {
"width": this.width,
"height": this.height,
"pixelFormat": options.pixelFormat,
"filter": this._options.filter,
"wrap": this._options.wrap,
"name": this.name + " cubemaptexture"
});
this.initializeRenderbuffers();
this.setSize(this.width, this.height);
}
initializeRenderbuffers()
{
this._framebuffer = this._cgl.gl.createFramebuffer(); // crate the framebuffer that will draw to the reflection map
this._depthbuffer = this._cgl.gl.createRenderbuffer(); // renderbuffer for depth buffer in framebuffer
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._framebuffer); // select the framebuffer, so we can attach the depth buffer to it
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, this._depthbuffer); // so we can create storage for the depthBuffer
this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER, this._cgl.gl.DEPTH_COMPONENT16, this.width, this.height);
this._cgl.gl.framebufferRenderbuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.DEPTH_ATTACHMENT, this._cgl.gl.RENDERBUFFER, this._depthbuffer);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, null);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, null);
}
getWidth()
{
return this.width;
}
getHeight()
{
return this.height;
}
getGlFrameBuffer()
{
return this._framebuffer;
}
getDepthRenderBuffer()
{
return this._depthRenderbuffer;
}
getTextureColor()
{
return this.texture;
}
getTextureDepth()
{
return this._textureDepth;
}
dispose()
{
if (this.texture) this.texture = this.texture.delete();
if (this._framebuffer) this._cgl.gl.deleteFramebuffer(this._framebuffer);
if (this._depthRenderbuffer) this._cgl.gl.deleteRenderbuffer(this._depthbuffer);
// // if (this._textureFrameBuffer) this._cgl.gl.deleteFramebuffer(this._textureFrameBuffer);
}
delete()
{
this.dispose();
}
setSize(width, height)
{
// console.log("cubemapframebuffer setsize");
this._cgl.printError("before cubemap setsize");
this.width = Math.floor(width);
this.height = Math.floor(height);
this.width = Math.min(this.width, this._cgl.maxTexSize);
this.height = Math.min(this.height, this._cgl.maxTexSize);
this._cgl.profileData.profileFrameBuffercreate++;
// if (this._framebuffer) this._cgl.gl.deleteFramebuffer(this._framebuffer);
// if (this._depthRenderbuffer) this._cgl.gl.deleteRenderbuffer(this._depthbuffer);
// // if (this._textureFrameBuffer) this._cgl.gl.deleteFramebuffer(this._textureFrameBuffer);
this._framebuffer = this._cgl.gl.createFramebuffer();
this._depthbuffer = this._cgl.gl.createRenderbuffer();
this.texture.setSize(this.width, this.height);
// this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_CUBE_MAP, this.texture.tex);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._framebuffer); // select the framebuffer, so we can attach the depth buffer to it
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, this._depthbuffer); // so we can create storage for the depthBuffer
this._cgl.gl.renderbufferStorage(this._cgl.gl.RENDERBUFFER, this._cgl.gl.DEPTH_COMPONENT16, this.width, this.height);
this._cgl.gl.framebufferRenderbuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.DEPTH_ATTACHMENT, this._cgl.gl.RENDERBUFFER, this._depthbuffer);
if (!this._cgl.gl.isFramebuffer(this._framebuffer))
{
console.error("invalid framebuffer...");
// throw new Error("Invalid framebuffer");
}
// * NOTE: if we check for the error in Safari, we get error code 36059 aka 0x8CDB
// * NOTE: an error that is found in a WebGL extension (WEBGL_draw_buffers) not supported by most iOS devices
// * NOTE: see https://gist.github.com/TimvanScherpenzeel/2a604e178013a5ac4b411fbcbfd2fa33
// * NOTE: also, this error is nowhere to be found in the official WebGL 1 spec
// if (this._cgl.glVersion !== 1)
// {
const status = this._cgl.gl.checkFramebufferStatus(this._cgl.gl.FRAMEBUFFER);
this.checkErrorsByStatus(status);
// }
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_CUBE_MAP, null);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, null);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, null);
this._cgl.printError("cubemap setsize");
}
checkErrorsByStatus(status)
{
switch (status)
{
case this._cgl.gl.FRAMEBUFFER_COMPLETE:
break;
case this._cgl.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
console.error("FRAMEBUFFER_INCOMPLETE_ATTACHMENT...", this.width, this.height, this.texture.tex, this._depthBuffer);
throw new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
case this._cgl.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
console.error("FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
throw new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
case this._cgl.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
console.error("FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
throw new Error("Incomplete framebuffer: FRAMEBUFFER_INCOMPLETE_DIMENSIONS");
case this._cgl.gl.FRAMEBUFFER_UNSUPPORTED:
console.error("FRAMEBUFFER_UNSUPPORTED");
throw new Error("Incomplete framebuffer: FRAMEBUFFER_UNSUPPORTED");
case 0x8CDB:
console.error("Incomplete: FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER from ext. Or Safari/iOS undefined behaviour.");
break;
default:
console.error("incomplete framebuffer", status);
console.log(this);
throw new Error("Incomplete framebuffer: " + status);
}
}
setFilter(filter)
{
this.texture.filter = filter;
this.texture.setSize(this.width, this.height);
}
setCamPos(camPos)
{
this.camPos = camPos || this.camPos;
}
setMatrices(M, V, P)
{
this._modelMatrix = M || this._modelMatrix;
this._viewMatrix = V || this._viewMatrix;
this._projectionMatrix = P || this._projectionMatrix;
}
renderStart()
{
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_CUBE_MAP, this.texture.tex);
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._framebuffer);
this._cgl.gl.bindRenderbuffer(this._cgl.gl.RENDERBUFFER, this._depthbuffer);
this._cgl.gl.viewport(0, 0, this.width, this.height);
this._cgl.pushGlFrameBuffer(this._framebuffer);
this._cgl.pushFrameBuffer(this);
}
renderStartCubemapFace(index)
{
this._cgl.pushModelMatrix();
this._cgl.pushViewMatrix();
this._cgl.pushPMatrix();
this._cgl.gl.framebufferTexture2D(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.COLOR_ATTACHMENT0, this._cubemapProperties[index].face, this.texture.tex, 0);
this._cgl.gl.framebufferRenderbuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.gl.DEPTH_ATTACHMENT, this._cgl.gl.RENDERBUFFER, this._depthbuffer);
if (this._options.clear)
{
this._cgl.gl.clearColor(0, 0, 0, 1);
this._cgl.gl.clear(this._cgl.gl.COLOR_BUFFER_BIT | this._cgl.gl.DEPTH_BUFFER_BIT);
}
this.setMatricesCubemapFace(index);
}
setMatricesCubemapFace(index)
{
mat4.copy(this._cgl.mMatrix, this._modelMatrix);
vec3.add(this._lookAtTemp, this.camPos, this._cubemapProperties[index].lookAt);
mat4.lookAt(this._cgl.vMatrix, this.camPos, this._lookAtTemp, this._cubemapProperties[index].up); // V
mat4.copy(this._cgl.pMatrix, this._projectionMatrix);
}
renderEndCubemapFace()
{
this._cgl.popPMatrix();
this._cgl.popModelMatrix();
this._cgl.popViewMatrix();
}
renderEnd()
{
this._cgl.profileData.profileFramebuffer++;
if (this._cgl.glVersion !== 1)
{
this._cgl.gl.bindFramebuffer(this._cgl.gl.READ_FRAMEBUFFER, this._framebuffer);
// this._cgl.gl.bindFramebuffer(this._cgl.gl.DRAW_FRAMEBUFFER, this._textureFrameBuffer);
// * NOTE: the line below is commented out because it clears the screen to black after
// * point light shadow map has been rendered
// this._cgl.gl.clearBufferfv(this._cgl.gl.COLOR, 0, [0.0, 0.0, 0.0, 1.0]);
}
this._cgl.gl.bindFramebuffer(this._cgl.gl.FRAMEBUFFER, this._cgl.popGlFrameBuffer());
this._cgl.popFrameBuffer();
this._cgl.resetViewPort();
this.updateMipMap();
}
updateMipMap()
{
if (!this.texture) return;
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_CUBE_MAP, this.texture.tex);
this.texture.updateMipMap();
this._cgl.gl.bindTexture(this._cgl.gl.TEXTURE_CUBE_MAP, null);
}
}
export { CubemapFramebuffer };