src/core/material.js
import Vao from '../renderer/helpers/vao';
import { createProgram } from '../renderer/helpers/program';
import { getContext, GL_TRIANGLES } from '../session';
const programs = {};
class Material {
constructor() {
this.defines = {};
this.attributes = Object.assign({
a_position: {
type: 'vec3',
value: null,
},
a_normal: {
type: 'vec3',
value: null,
},
a_uv: {
type: 'vec2',
value: null,
},
});
this.uniforms = {};
this.vertex = '';
this.fragment = '';
this.vao = new Vao();
this.program = null;
this.indices = null;
this.glMode = GL_TRIANGLES;
}
createProgram() {
const gl = getContext();
const key = `shader-${this.type}`;
if (programs[key] === undefined) {
const program = createProgram(gl, this.vertex, this.fragment);
programs[key] = program;
}
this.program = programs[key];
}
initAttributes() {
const gl = getContext();
for (const prop in this.attributes) {
const current = this.attributes[prop];
const location = gl.getAttribLocation(this.program, prop);
const buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, current.value, gl.DYNAMIC_DRAW); // STATIC_DRAW
let size;
switch (current.type) {
case 'vec3':
size = 3;
break;
case 'vec2':
size = 2;
break;
default:
size = 1;
}
Object.assign(current, {
location,
buffer,
size,
});
}
if (this.indices) {
this.indexBuffer = gl.createBuffer();
}
}
initUniforms() {
const gl = getContext();
const textureIndices = [
gl.TEXTURE0,
gl.TEXTURE1,
gl.TEXTURE2,
gl.TEXTURE3,
gl.TEXTURE4,
gl.TEXTURE5,
];
Object.keys(this.uniforms).forEach((prop, i) => {
const current = this.uniforms[prop];
const location = gl.getUniformLocation(this.program, prop);
if (current.type === 'sampler2D') {
current.textureIndex = i;
current.activeTexture = textureIndices[i];
}
current.location = location;
});
}
init() {
this.createProgram();
this.initAttributes();
this.initUniforms();
this.vao.bind();
this.bind();
this.vao.unbind();
this.unbind();
}
destroy() {
// destroy buffers
}
bind() {
const gl = getContext();
Object.keys(this.attributes).forEach((key) => {
const { location, buffer, size } = this.attributes[key];
if (location !== -1) {
gl.enableVertexAttribArray(location);
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
}
});
if (this.indices) {
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW);
}
}
unbind() {
const gl = getContext();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
update() {
// update uniforms
const gl = getContext();
Object.keys(this.uniforms).forEach((key) => {
const uniform = this.uniforms[key];
switch (uniform.type) {
case 'mat4':
gl.uniformMatrix4fv(uniform.location, false, uniform.value);
break;
case 'mat3':
gl.uniformMatrix3fv(uniform.location, false, uniform.value);
break;
case 'vec4':
gl.uniform4fv(uniform.location, uniform.value);
break;
case 'vec3':
gl.uniform3fv(uniform.location, uniform.value);
break;
case 'vec2':
gl.uniform2fv(uniform.location, uniform.value);
break;
case 'float':
gl.uniform1f(uniform.location, uniform.value);
break;
case 'sampler2D':
gl.uniform1i(uniform.location, uniform.textureIndex);
gl.activeTexture(uniform.activeTexture);
gl.bindTexture(gl.TEXTURE_2D, uniform.value);
break;
default:
console.warn('unknown uniform type', uniform.type);
}
});
}
}
export default Material;