meta-next
Version:
182 lines (156 loc) • 4.54 kB
JavaScript
import Engine from "../Engine"
import Vector3 from "../math/Vector3"
import Vector4 from "../math/Vector4"
import Matrix4 from "../math/Matrix4"
import Texture from "../resources/Texture"
import DrawCommand from "./DrawCommand"
let activeMaterial = null
const emptyVector3 = new Vector3()
const emptyVector4 = new Vector4()
const emptyMatrix = new Matrix4()
let emptyTexture = null
let emptyPropsCreated = false
class Renderer
{
constructor() {
Engine.on("setup", this.setup.bind(this))
}
setup()
{
if(!emptyPropsCreated) {
emptyPropsCreated = true
emptyTexture = new Texture()
emptyTexture.loadEmpty()
}
}
render(drawCommand)
{
const gl = Engine.gl
const mesh = drawCommand.mesh
this.useMaterial(drawCommand.material)
this.updateAttribs(drawCommand)
this.updateUniforms(drawCommand)
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.indices)
gl.drawElements(drawCommand.mode, mesh.numElements, gl.UNSIGNED_SHORT, 0)
}
useMaterial(material)
{
if(activeMaterial === material) { return }
const gl = Engine.gl
gl.useProgram(material.program)
const currNumAttribs = this.activeMaterial ? this.activeMaterial.numAttribs : 0
const newNumAttribs = material.numAttribs
if(currNumAttribs < newNumAttribs) {
for(let n = currNumAttribs; n < newNumAttribs; n++) {
gl.enableVertexAttribArray(n)
}
}
else {
for(let n = newNumAttribs; n < currNumAttribs; n++) {
gl.disableVertexAttribArray(n)
}
}
activeMaterial = material
}
updateAttribs(drawCommand)
{
const gl = Engine.gl
const mesh = drawCommand.mesh
const attribData = drawCommand.material.attribData
for(let n = 0; n < attribData.length; n++) {
const attrib = attribData[n]
switch(attrib.name)
{
case "position":
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffer)
gl.vertexAttribPointer(attrib.loc, 2, gl.FLOAT, false, mesh.stride, 0)
break
case "uv":
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.buffer);
gl.vertexAttribPointer(attrib.loc, 2, gl.FLOAT, false, mesh.stride, 8)
break
}
}
}
updateUniforms(drawCommand)
{
let numSamplers = 0
const gl = Engine.gl
const material = drawCommand.material
const uniforms = drawCommand.uniforms
const uniformData = material.uniformData
for(let n = 0; n < uniformData.length; n++) {
const uniform = uniformData[n]
switch(uniform.type)
{
case gl.FLOAT_MAT4:
{
let matrix
switch(uniform.name)
{
case "matrixProjection":
matrix = Engine.camera.projectionTransform
break
case "matrixView":
matrix = Engine.camera.transform
break
case "matrixModel":
matrix = drawCommand.transform
break
default:
matrix = material.uniforms[uniform.name]
break
}
if(matrix) {
gl.uniformMatrix4fv(uniform.loc, false, matrix.m)
}
else {
gl.uniformMatrix4fv(uniform.loc, false, emptyMatrix.m)
console.warn(`(Renderer.updateUniforms) Empty FLOAT_MAT4 uniform used for: ${uniform.name}`)
}
} break
case gl.FLOAT_VEC3:
{
const vec = uniforms[uniform.name]
if(vec) {
gl.uniform3fv(uniform.loc, vec.toFloat32Array())
}
else {
gl.uniform3fv(uniform.loc, emptyVector3.toFloat32Array())
console.warn(`(Renderer.updateUniforms) Empty FLOAT_VEC3 uniform used for: ${uniform.name}`)
}
} break
case gl.FLOAT_VEC4:
{
const vec = uniforms[uniform.name]
if(vec) {
gl.uniform4fv(uniform.loc, vec.toFloat32Array())
}
else {
gl.uniform4fv(uniform.loc, emptyVector4.toFloat32Array())
console.warn(`(Renderer.updateUniforms) Empty FLOAT_VEC4 uniform used for: ${uniform.name}`)
}
} break
case gl.SAMPLER_2D:
{
const texture = uniforms[uniform.name]
gl.activeTexture(gl.TEXTURE0 + numSamplers)
if(texture) {
gl.bindTexture(gl.TEXTURE_2D, texture)
}
else {
gl.bindTexture(gl.TEXTURE_2D, emptyTexture.instance)
console.warn(`(Renderer.updateUniforms) Empty SAMPLER_2D uniform used for: ${uniform.name}`)
}
gl.uniform1i(uniform.loc, numSamplers++)
} break
case gl.FLOAT:
{
const float = uniforms[uniform.name]
gl.uniform1f(uniform.loc, float || 0)
} break
}
}
}
}
export default Renderer