// state object// var setVertexAttribArrays = require( './setVertexAttribArrays' ); /** * Helper class to work with WebGL VertexArrayObjects (vaos) * Only works if WebGL extensions are enabled (they usually are) * * @class * @memberof PIXI.glCore * @param gl {WebGLRenderingContext} The current WebGL rendering context */ function VertexArrayObject(gl, state) { this.nativeVaoExtension = null; if(!VertexArrayObject.FORCE_NATIVE) { this.nativeVaoExtension = gl.getExtension('OES_vertex_array_object') || gl.getExtension('MOZ_OES_vertex_array_object') || gl.getExtension('WEBKIT_OES_vertex_array_object'); this.instanceExt = gl.getExtension("ANGLE_instanced_arrays"); } this.nativeState = state; if(this.nativeVaoExtension) { this.nativeVao = this.nativeVaoExtension.createVertexArrayOES(); var maxAttribs = gl.getParameter(gl.MAX_VERTEX_ATTRIBS); // VAO - overwrite the state.. this.nativeState = { tempAttribState: new Array(maxAttribs), attribState: new Array(maxAttribs) }; } /** * The current WebGL rendering context * * @member {WebGLRenderingContext} */ this.gl = gl; /** * An array of attributes * * @member {Array} */ this.attributes = []; /** * @member {PIXI.glCore.GLBuffer} */ this.indexBuffer = null; /** * A boolean flag * * @member {Boolean} */ this.dirty = false; } VertexArrayObject.prototype.constructor = VertexArrayObject; module.exports = VertexArrayObject; /** * Some devices behave a bit funny when using the newer extensions (im looking at you ipad 2!) * If you find on older devices that things have gone a bit weird then set this to true. */ /** * Lets the VAO know if you should use the WebGL extension or the native methods. * Some devices behave a bit funny when using the newer extensions (im looking at you ipad 2!) * If you find on older devices that things have gone a bit weird then set this to true. * @static * @property {Boolean} FORCE_NATIVE */ VertexArrayObject.FORCE_NATIVE = false; /** * Binds the buffer */ VertexArrayObject.prototype.bind = function() { if(this.nativeVao) { this.nativeVaoExtension.bindVertexArrayOES(this.nativeVao); if(this.dirty) { this.dirty = false; this.activate(); } } else { this.activate(); } return this; }; /** * Unbinds the buffer */ VertexArrayObject.prototype.unbind = function() { if(this.nativeVao) { this.nativeVaoExtension.bindVertexArrayOES(null); } return this; }; /** * Uses this vao */ VertexArrayObject.prototype.activate = function() { var gl = this.gl; var lastBuffer = null; for (var i = 0; i < this.attributes.length; i++) { var attrib = this.attributes[i]; if(lastBuffer !== attrib.buffer) { attrib.buffer.bind(); lastBuffer = attrib.buffer; } gl.vertexAttribPointer(attrib.attribute.location, attrib.attribute.size, attrib.type || gl.FLOAT, attrib.normalized || false, attrib.stride || 0, attrib.start || 0); if(attrib.instance) { if(this.instanceExt) { this.instanceExt.vertexAttribDivisorANGLE(attrib.attribute.location, 1); } else { console.warn('instancing not supported by this device :/') } } } setVertexAttribArrays(gl, this.attributes, this.nativeState); if(this.indexBuffer) { this.indexBuffer.bind(); } return this; }; /** * * @param buffer {PIXI.gl.GLBuffer} * @param attribute {*} * @param type {String} * @param normalized {Boolean} * @param stride {Number} * @param start {Number} */ VertexArrayObject.prototype.addAttribute = function(buffer, attribute, type, normalized, stride, start, instance) { this.attributes.push({ buffer: buffer, attribute: attribute, location: attribute.location, type: type || this.gl.FLOAT, normalized: normalized || false, stride: stride || 0, start: start || 0, instance: instance }); this.instancedMesh = this.instancedMesh || instance; this.dirty = true; return this; }; /** * * @param buffer {PIXI.gl.GLBuffer} */ VertexArrayObject.prototype.addIndex = function(buffer/*, options*/) { this.indexBuffer = buffer; this.dirty = true; return this; }; /** * Unbinds this vao and disables it */ VertexArrayObject.prototype.clear = function() { // var gl = this.gl; // TODO - should this function unbind after clear? // for now, no but lets see what happens in the real world! if(this.nativeVao) { this.nativeVaoExtension.bindVertexArrayOES(this.nativeVao); } this.attributes.length = 0; this.indexBuffer = null; return this; }; /** * @param type {Number} * @param size {Number} * @param start {Number} */ VertexArrayObject.prototype.draw = function(type, size, start, instanceCount) { var gl = this.gl; if(this.indexBuffer) { if(this.instancedMesh) { this.instanceExt.drawElementsInstancedANGLE(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2, instanceCount || 1); } else { gl.drawElements(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2 ); } } else { if(this.instancedMesh) { // TODO need a better way to calculate size.. this.instanceExt.drawArrayInstancedANGLE(type, start, size || this.getSize(), instanceCount || 1); } else { gl.drawArrays(type, start, size || this.getSize()); } } return this; }; /** * Destroy this vao */ VertexArrayObject.prototype.destroy = function() { // lose references this.gl = null; this.indexBuffer = null; this.attributes = null; this.nativeState = null; if(this.nativeVao) { this.nativeVaoExtension.deleteVertexArrayOES(this.nativeVao); } this.nativeVaoExtension = null; this.nativeVao = null; }; VertexArrayObject.prototype.getSize = function() { var attrib = this.attributes[0]; return attrib.buffer.data.length / (( attrib.stride/4 ) || attrib.attribute.size); };