import { ColorProps } from "../data/props/ColorProps";
import { Framebuffer } from "../data/texture/Framebuffer";
import { noop } from "../utils/helpers";
import { Buffer } from "../utils/Buffer";
import { Utils, Const } from "../utils/Utils";
/**
* @typedef {Object} BaseRendererConfig
* @extends {RendererConfig}
* @property {Object} config
* @property {ColorProps} clearColor
* @property {WebGL2Context} context
* @property {boolean} resized
* @property {number} width
* @property {number} height
* @property {number} widthHalf
* @property {number} heightHalf
*/
/**
* Base renderer
*/
export class BaseRenderer {
/**
* Creates an instance of BaseRenderer.
* @constructor
* @param {BaseRendererConfig} options
*/
constructor(options) {
this._clearBeforeRenderFunc = noop;
this._resizeCalcFv = noop;
this._rendererId = 1;
this.width =
this.height =
this.widthHalf =
this.heightHalf =
this._currentContextId =
this._currentRendererId =
this.$renderTime =
0;
this.setSize(1, 1);
this.clearColor = new ColorProps();
this._options = options;
const config = this._options.config;
this.context = config.context;
// prettier-ignore
config.locations = [
...config.locations,
"uFlpY",
"aPos",
"uTex"
];
this._elementArrayBuffer = new Buffer(
"",
new Uint16Array([0, 1, 3, 2]),
0,
0,
Const.ELEMENT_ARRAY_BUFFER,
Const.STATIC_DRAW
);
// prettier-ignore
this._positionBuffer = new Buffer(
"aPos", new Float32Array([
0, 0,
1, 0,
1, 1,
0, 1
]),
1, 2,
Const.ARRAY_BUFFER,
Const.STATIC_DRAW,
0
);
}
/**
* Set clear before render
* @param {boolean} v
*/
set clearBeforeRender(v) {
this._clearBeforeRenderFunc = v ? this._clear : noop;
}
/**
* Set size
* @param {number} width
* @param {number} height
*/
setSize(width, height) {
this.width = width;
this.height = height;
this._resizeCalcFv = this.$resize;
}
/**
* Render to framebuffer
* @param {Framebuffer} framebuffer
*/
renderToFramebuffer(framebuffer) {
if (!this.context.isLost()) {
this._switchToProgram();
this.$attachFramebufferAndClear(framebuffer);
this._renderBatch(framebuffer);
framebuffer.unbind(this.$gl);
}
}
/**
* Render
*/
render() {
if (!this.context.isLost()) {
this._switchToProgram();
this.$gl.uniform1f(this.$locations.uFlpY, 1);
this._clearBeforeRenderFunc();
this._renderBatch();
}
}
/**
* Destruct class
*/
destruct() {}
/**
* @param {Framebuffer} framebuffer
* @ignore
*/
$attachFramebuffer(framebuffer) {
framebuffer.bind(this.$gl);
framebuffer.setSize(this.width, this.height);
this.context.useTexture(framebuffer, this.$renderTime, false);
this.context.deactivateTexture(framebuffer);
this.$gl.uniform1f(this.$locations.uFlpY, -1);
}
/**
* @param {Framebuffer} framebuffer
* @ignore
*/
$attachFramebufferAndClear(framebuffer) {
this.$attachFramebuffer(framebuffer);
this._clearBeforeRenderFunc();
}
/**
* @param {number} count
* @ignore
*/
$drawInstanced(count) {
this.$gl.drawElementsInstanced(
Const.TRIANGLE_STRIP,
4,
Const.UNSIGNED_SHORT,
0,
count
);
}
/**
* @param {TextureInfo} texture
* @param {number} location
* @param {number} index
* @ignore
*/
$useTextureAt(texture, location, index, forceBind) {
this.$gl.uniform1i(
location,
this.context.useTextureAt(texture, index, this.$renderTime, forceBind)
);
}
/**
* @param {TextureInfo} texture
* @param {number} location
* @ignore
*/
$useTexture(texture, location, forceBind) {
this.$gl.uniform1i(
location,
this.context.useTexture(texture, this.$renderTime, forceBind)
);
}
/**
* @ignore
*/
$uploadBuffers() {
const gl = this.$gl;
this._positionBuffer.upload(gl, this.$enableBuffers);
this._elementArrayBuffer.upload(gl);
this.$enableBuffers = false;
}
/**
* @ignore
*/
$createBuffers() {
const gl = this.$gl,
locations = this.$locations;
this._elementArrayBuffer.create(gl, locations);
this._positionBuffer.create(gl, locations);
}
/**
* @ignore
*/
$resize() {
this.resized = true;
this._resizeCalcFv = noop;
this.widthHalf = this.width / 2;
this.heightHalf = this.height / 2;
}
/**
* @param {Framebuffer} framebuffer
* @ignore
*/
_renderBatch(framebuffer) {
this.$renderTime = Date.now();
this.$render(framebuffer);
}
/**
* @ignore
*/
_switchToProgram() {
this.$gl = this.context.gl;
const context = this.context,
contextId = context.contextId;
if (
this._currentContextId !== contextId ||
this._currentRendererId !== this._rendererId
) {
this._currentContextId = contextId;
this._currentRendererId = this._rendererId;
this._buildProgram();
} else this._useProgram();
this.resized = false;
this._resizeCalcFv();
this.context.setSize(this.width, this.height);
}
/**
* @ignore
*/
_clear() {
const gl = this.$gl,
color = this.clearColor;
this.$gl.clearColor(color.r, color.g, color.b, color.a);
this.$gl.clear(Const.COLOR_BUFFER_BIT);
}
/**
* @ignore
*/
_buildProgram() {
const gl = this.$gl,
shaderHeader = Utils.GLSL.VERSION + "precision highp float;\n";
this._program = Utils.createProgram(
gl,
shaderHeader + this.$createVertexShader(),
shaderHeader + this.$createFragmentShader()
);
this.$locations = Utils.getLocationsFor(
gl,
this._program,
this._options.config.locations
);
this._vao = gl.createVertexArray();
this._useProgram();
this.$createBuffers();
}
/**
* @ignore
*/
_useProgram() {
if (this.context.useProgram(this._program, this._vao))
this.$enableBuffers = true;
}
}