/**
* Creates new buffer for object3D
* @param {Object} gl
* @param {Ayce.Object3D} object3D
* @param {Ayce.Shader} shader
* @param {Array} attributes
* @param {Array} uniforms
* @param {Number} drawMode
* @class
* @constructor
*/
Ayce.Buffer = function (gl, object3D, shader, attributes, uniforms, drawMode) {
drawMode = drawMode || gl.TRIANGLES;
var textures = [];
var specularMaps = [];
var normalMaps = [];
var vertexIndexBuffer;
var interlacedBuffer;
var indicesSize;
var vao;
this.indices = null;
this.useTexture = false;
this.useSpecularMap = false;
this.useTransparency = false;
this.texturesO3D = null;
this.isSkybox = false;
var texturesLoading = false;
var specularMapsLoading = false;
var normalMapsLoading = false;
var i = 0;
var stride = 0;
/**
* *******************************************
* Buffer initialization
* *******************************************
*/
this.init = function() {
texturesLoading = this.useTexture;
specularMapsLoading = this.useSpecularMap;
normalMapsLoading = this.useNormalMap;
//Get Shader Attribute Locations
var a;
var offset = 0;
for(i=0; i<attributes.length; i++){
a = attributes[i];
a[3] = shader.getAttribLocation(a[0]);
a[4] = offset;
offset += a[1]*4;
stride += a[1]*4;
}
//Get Shader Uniform Locations
for(i=0; i < uniforms.length; i++){
var u = uniforms[i];
u[4] = new Array(1+u[3].length);
u[4][0] = shader.getUniformLocation(u[0]);
}
//Bind Textures
if (this.useTexture) {
shader.samplerUniform = shader.getUniformLocation("uSampler");
for(i=0; i<this.texturesO3D.length; i++){
textures.push(this.loadTexture(gl, this.texturesO3D[i], this.isSkybox));
}
}
if(this.useSpecularMap){
this.texturesO3D = (Array.isArray(object3D.specularMap)) ? object3D.specularMap : [object3D.specularMap];
shader.specularMapUniform = shader.getUniformLocation("uSpecularMapSampler");
for(i=0; i<this.texturesO3D.length; i++){
specularMaps.push(this.loadTexture(gl, this.texturesO3D[i], this.isSkybox));
}
}
if(this.useNormalMap){
this.texturesO3D = (Array.isArray(object3D.normalMap)) ? object3D.normalMap : [object3D.normalMap];
shader.normalMapUniform = shader.getUniformLocation("uNormalMapSampler");
for(i=0; i<this.texturesO3D.length; i++){
normalMaps.push(this.loadTexture(gl, this.texturesO3D[i], this.isSkybox));
}
}
//Interlaced array generation
var interlacedArray = [];
for (var index=0; index < attributes[0][2].length/attributes[0][1]; index++) {
for(i=0; i<attributes.length; i++){
a = attributes[i];
this.pushData(interlacedArray, a[2], index, a[1]);
}
}
//Bind interlaced Buffer
interlacedBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, interlacedBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(interlacedArray), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
//Bind Index VBO
vertexIndexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.indices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
indicesSize = this.indices.length;
//Bind VAO
if(gl.ext){
vao = gl.ext.createVertexArrayOES();
gl.ext.bindVertexArrayOES(vao);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.bindBuffer(gl.ARRAY_BUFFER, interlacedBuffer);
//Enable attributes
for(i=0; i<attributes.length; i++){
a = attributes[i];
this.setupAttribPointer(gl, a[3], a[1], stride, a[4]);
offset += a[1]*4;
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.ext.bindVertexArrayOES(null);
}
};
/**
* *******************************************
* Render function
* *******************************************
*/
this.update = function () {
if(texturesLoading) {texturesLoading = areTexturesLoaded(texturesLoading, textures);}
if(specularMapsLoading) {specularMapsLoading = areTexturesLoaded(specularMapsLoading, specularMaps);}
if(normalMapsLoading) {normalMapsLoading = areTexturesLoaded(normalMapsLoading, normalMaps);}
// for(i=0; i < uniforms.length; i++){
// var u = uniforms[i];
// u[4][1] = u[3];
//
// if(u[2]){
// for(var j=0; j<u[3].length; j++){
// u[4][j+1] = u[2][u[3][j]];
// }
// }
// }
};
/**
* Indicates if all textures are done loading
* @param {Boolean} currentStatus
* @param {Object[]} textures
* @return {Boolean} currentStatus
*/
var areTexturesLoaded = function(currentStatus, textures){
if(currentStatus){
var allLoaded = true;
for(i=0; i < textures.length; i++){
if(!textures[i].loaded){
allLoaded = false;
break;
}
}
currentStatus = !allLoaded;
}
return currentStatus;
};
/**
* Renders buffer
*/
this.render = function () {
//TODO Alternative Texture?
if(texturesLoading || specularMapsLoading || normalMapsLoading)return;
//Handle transparency
if(this.useTransparency){
gl.enable(gl.BLEND);
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
}
//Init Shader Program
shader.initShaders();
//Set Texture active
if (this.useTexture) {
this.activateTextures(gl, shader.samplerUniform, textures);
}
//Set SpecularMap texture active
if (this.useSpecularMap) {
for(i=0; i<specularMaps.length; i++){
//console.log(textures.length);
gl.activeTexture(gl.TEXTURE0+textures.length+i);
gl.bindTexture(gl.TEXTURE_2D, specularMaps[i]);
}
gl.uniform1i(shader.specularMapUniform, 1);
}
//Set NormalMap texture active
if (this.useNormalMap) {
for(i=0; i<normalMaps.length; i++){
gl.activeTexture(gl.TEXTURE0+textures.length+i);
gl.bindTexture(gl.TEXTURE_2D, normalMaps[i]);
}
gl.uniform1i(shader.normalMapUniform, 1);
}
// Set uniforms
for(i=0; i < uniforms.length; i++){
var u = uniforms[i];
u[4][1] = u[3];
if(u[2]){
for(var j=0; j<u[3].length; j++){
u[4][j+1] = u[2][u[3][j]];
}
}
}
for(i=0; i < uniforms.length; i++){
gl[uniforms[i][1]].apply(gl, uniforms[i][4]);
}
//Bind VAO and Draw O3D
if(gl.ext){
gl.ext.bindVertexArrayOES(vao);
gl.drawElements(drawMode, indicesSize, gl.UNSIGNED_SHORT, 0);
gl.ext.bindVertexArrayOES(null);
}
else{
gl.bindBuffer(gl.ARRAY_BUFFER, interlacedBuffer);
//Enable attributes
for(i=0; i<attributes.length; i++){
var a = attributes[i];
this.setupAttribPointer(gl, a[3], a[1], stride, a[4]);
}
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vertexIndexBuffer);
gl.drawElements(drawMode, indicesSize, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
}
// gl.useProgram(null);
if(this.useTransparency){
gl.disable(gl.BLEND);
}
};
this.dispose = function(){
gl.deleteBuffer(vertexIndexBuffer);
gl.deleteBuffer(interlacedBuffer);
// for(i=0; i < textures.length; i++){
// gl.deleteTexture(textures[i]);
// }
};
};
Ayce.Buffer.prototype = {
/**
* Adds data to interlaced Array
* @param {Array} array
* @param {Number[]} values
* @param {Number} index
* @param {Number} numItems
*/
pushData: function(array, values, index, numItems){
var i;
for (i = 0; i < numItems; i++) {
array.push(values[index*numItems+i]);
}
},
/**
* Enables attribute and sets attribute pointer
* @param {Object} gl
* @param {Number} attribute
* @param {Number} numValues
* @param {Number} stride
* @param {Number} offset
*/
setupAttribPointer: function(gl, attribute, numValues, stride, offset){
// console.log(attribute);
gl.enableVertexAttribArray(attribute);
gl.vertexAttribPointer(attribute, numValues, gl.FLOAT, false, stride, offset);
},
loadedTextures: {},
/**
* Description
* @param {Object} gl
* @param {Object} source
* @param {Boolean} clampToEdge
* @return {Object} texture
*/
loadTexture: function(gl, source, clampToEdge){
// generate texture
var texture = gl.createTexture();
//Bind Texture
texture.loaded = false;
var wrapMode = gl.REPEAT;
if(clampToEdge || source.clamp) wrapMode = gl.CLAMP_TO_EDGE;
source = source.source ? source.source : source;
if(this.loadedTextures[source]){
texture.loaded = true;
return this.loadedTextures[source];
}
else{
texture.image = new Image();
texture.image.src = source;
/**
* Description
*/
texture.image.onload = function () {
if(false){
var canvas = document.createElement("canvas");
var a = Math.min(texture.image.width, texture.image.height)/256;
canvas.width = texture.image.width/a;
canvas.height = texture.image.height/a;
var ctx = canvas.getContext("2d");
ctx.drawImage(texture.image, 0, 0, canvas.width, canvas.height);
texture.image = canvas;
}
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, wrapMode);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, wrapMode);
gl.generateMipmap(gl.TEXTURE_2D);
gl.bindTexture(gl.TEXTURE_2D, null);
texture.loaded = true;
};
this.loadedTextures[source] = texture;
}
return texture;
},
/**
* Activates and binds texture
* @param {Object} gl
* @param {String} samplerUniform
* @param {Object[]} textures
*/
activateTextures: function(gl, samplerUniform, textures){
var a = [];
for(var i=0; i<textures.length; i++){
gl.activeTexture(gl.TEXTURE0+i);
gl.bindTexture(gl.TEXTURE_2D, textures[i]);
a.push(i);
}
gl.uniform1iv(samplerUniform, a);
}
};