diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js index 8849245..ec9cd9c 100644 --- a/src/core/renderers/webgl/systems/ContextSystem.js +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -75,7 +75,8 @@ */ createContext(canvas, options) { - var gl = canvas.getContext('webgl', options) || + var gl = canvas.getContext('webgl2', options) || + canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options); if (!gl) diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js index 8849245..ec9cd9c 100644 --- a/src/core/renderers/webgl/systems/ContextSystem.js +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -75,7 +75,8 @@ */ createContext(canvas, options) { - var gl = canvas.getContext('webgl', options) || + var gl = canvas.getContext('webgl2', options) || + canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options); if (!gl) diff --git a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js index 837ba94..0f2138b 100644 --- a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js +++ b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js @@ -22,7 +22,9 @@ { super(renderer); + this._activeGeometry = null; this._activeVao = null; + } /** @@ -43,9 +45,28 @@ */ bind(geometry, glShader) { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + const gl = this.gl; - this.bindVao(vao); + // not sure the best way to address this.. + // currently different shaders require different VAOs for the same geometry + // Still mulling over the best way to solve this one.. + // will likely need to modify the shader attribute locations at run time! + let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + + if(!vaos) + { + vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID] = {}; + } + + const vao = vaos[glShader.id] || this.initGeometryVao(geometry, glShader); + + this._activeGeometry = geometry; + + if(this._activeVao !== vao) + { + this._activeVao = vao; + gl.bindVertexArray(vao); + } // TODO - optimise later! // don't need to loop through if nothing changed! @@ -65,6 +86,21 @@ } } + checkCompatability(geometry, glShader) + { + // geometry must have at least all the attributes that the shader requires. + const geometryAttributes = geometry.attributes; + const shaderAttributes = glShader.attributes; + + for (const j in shaderAttributes) + { + if(!geometryAttributes[j]) + { + throw new Error('shader and geometry incompatible, geometry missing the "' + j + '" attribute'); + } + } + } + /** * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private @@ -73,12 +109,10 @@ */ initGeometryVao(geometry, glShader) { + this.checkCompatability(geometry, glShader); + const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - + const CONTEXT_UID = this.CONTEXT_UID; const buffers = geometry.buffers; const attributes = geometry.attributes; @@ -87,26 +121,20 @@ { const buffer = buffers[i]; - if (!buffer._glBuffers[this.CONTEXT_UID]) + if (!buffer._glBuffers[CONTEXT_UID]) { if (buffer.index) { - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); } else { /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); } } } - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - const tempStride = {}; const tempStart = {}; @@ -118,17 +146,24 @@ for (const j in attributes) { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + if(!attributes[j].size && glShader.attributes[j]) + { + attributes[j].size = glShader.attributes[j].size; + } + + tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type]; } for (const j in attributes) { const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; + const attribSize = attribute.size; + + // must be careful that the geometry has attributes not used by this shader.. if (attribute.stride === undefined) { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) { attribute.stride = 0; } @@ -142,37 +177,95 @@ { attribute.start = tempStart[attribute.buffer]; - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]; } } - // next update the attributes buffer.. + const vao = gl.createVertexArray(); + + gl.bindVertexArray(vao); + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + geometry.indexBuffer._glBuffers[CONTEXT_UID].bind(); + } + + let lastBuffer = null; + + // add a new one! for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; + const glBuffer = buffer._glBuffers[CONTEXT_UID]; - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + if(glShader.attributes[j]) + { + if(lastBuffer !== glBuffer) + { + glBuffer.bind(); + lastBuffer = glBuffer; + } - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); + const location = glShader.attributes[j].location; + + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, + attribute.size, + attribute.type || gl.FLOAT, + attribute.normalized, + attribute.stride, + attribute.start); + + if(attribute.instance) + { + gl.vertexAttribDivisor(location, 1); + } + } } - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + geometry.glVertexArrayObjects[CONTEXT_UID][glShader.id] = vao; + + gl.bindVertexArray(null); return vao; } + draw(type, size, start, instanceCount) { - this._activeVao.draw(type, size, start, instanceCount); + const gl = this.gl; + const geometry = this._activeGeometry; + + //TODO.. this should not change so maybe cache the function? + + if(geometry.indexBuffer) + { + if(geometry.instanced) + { + gl.drawElementsInstanced(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2, instanceCount || 1); + } + else + { + gl.drawElements(type, size || geometry.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2 ); + } + } + else + { + if(geometry.instanced) + { + // TODO need a better way to calculate size.. + gl.drawArrayInstanced(type, start, size || geometry.getSize(), instanceCount || 1); + } + else + { + gl.drawArrays(type, start, size || geometry.getSize()); + } + } + + return this; } /** @@ -193,23 +286,5 @@ */ bindVao(vao) { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; } } diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js index 8849245..ec9cd9c 100644 --- a/src/core/renderers/webgl/systems/ContextSystem.js +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -75,7 +75,8 @@ */ createContext(canvas, options) { - var gl = canvas.getContext('webgl', options) || + var gl = canvas.getContext('webgl2', options) || + canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options); if (!gl) diff --git a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js index 837ba94..0f2138b 100644 --- a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js +++ b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js @@ -22,7 +22,9 @@ { super(renderer); + this._activeGeometry = null; this._activeVao = null; + } /** @@ -43,9 +45,28 @@ */ bind(geometry, glShader) { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + const gl = this.gl; - this.bindVao(vao); + // not sure the best way to address this.. + // currently different shaders require different VAOs for the same geometry + // Still mulling over the best way to solve this one.. + // will likely need to modify the shader attribute locations at run time! + let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + + if(!vaos) + { + vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID] = {}; + } + + const vao = vaos[glShader.id] || this.initGeometryVao(geometry, glShader); + + this._activeGeometry = geometry; + + if(this._activeVao !== vao) + { + this._activeVao = vao; + gl.bindVertexArray(vao); + } // TODO - optimise later! // don't need to loop through if nothing changed! @@ -65,6 +86,21 @@ } } + checkCompatability(geometry, glShader) + { + // geometry must have at least all the attributes that the shader requires. + const geometryAttributes = geometry.attributes; + const shaderAttributes = glShader.attributes; + + for (const j in shaderAttributes) + { + if(!geometryAttributes[j]) + { + throw new Error('shader and geometry incompatible, geometry missing the "' + j + '" attribute'); + } + } + } + /** * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private @@ -73,12 +109,10 @@ */ initGeometryVao(geometry, glShader) { + this.checkCompatability(geometry, glShader); + const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - + const CONTEXT_UID = this.CONTEXT_UID; const buffers = geometry.buffers; const attributes = geometry.attributes; @@ -87,26 +121,20 @@ { const buffer = buffers[i]; - if (!buffer._glBuffers[this.CONTEXT_UID]) + if (!buffer._glBuffers[CONTEXT_UID]) { if (buffer.index) { - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); } else { /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); } } } - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - const tempStride = {}; const tempStart = {}; @@ -118,17 +146,24 @@ for (const j in attributes) { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + if(!attributes[j].size && glShader.attributes[j]) + { + attributes[j].size = glShader.attributes[j].size; + } + + tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type]; } for (const j in attributes) { const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; + const attribSize = attribute.size; + + // must be careful that the geometry has attributes not used by this shader.. if (attribute.stride === undefined) { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) { attribute.stride = 0; } @@ -142,37 +177,95 @@ { attribute.start = tempStart[attribute.buffer]; - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]; } } - // next update the attributes buffer.. + const vao = gl.createVertexArray(); + + gl.bindVertexArray(vao); + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + geometry.indexBuffer._glBuffers[CONTEXT_UID].bind(); + } + + let lastBuffer = null; + + // add a new one! for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; + const glBuffer = buffer._glBuffers[CONTEXT_UID]; - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + if(glShader.attributes[j]) + { + if(lastBuffer !== glBuffer) + { + glBuffer.bind(); + lastBuffer = glBuffer; + } - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); + const location = glShader.attributes[j].location; + + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, + attribute.size, + attribute.type || gl.FLOAT, + attribute.normalized, + attribute.stride, + attribute.start); + + if(attribute.instance) + { + gl.vertexAttribDivisor(location, 1); + } + } } - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + geometry.glVertexArrayObjects[CONTEXT_UID][glShader.id] = vao; + + gl.bindVertexArray(null); return vao; } + draw(type, size, start, instanceCount) { - this._activeVao.draw(type, size, start, instanceCount); + const gl = this.gl; + const geometry = this._activeGeometry; + + //TODO.. this should not change so maybe cache the function? + + if(geometry.indexBuffer) + { + if(geometry.instanced) + { + gl.drawElementsInstanced(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2, instanceCount || 1); + } + else + { + gl.drawElements(type, size || geometry.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2 ); + } + } + else + { + if(geometry.instanced) + { + // TODO need a better way to calculate size.. + gl.drawArrayInstanced(type, start, size || geometry.getSize(), instanceCount || 1); + } + else + { + gl.drawArrays(type, start, size || geometry.getSize()); + } + } + + return this; } /** @@ -193,23 +286,5 @@ */ bindVao(vao) { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; } } diff --git a/src/core/renderers/webgl/systems/shader/GLShader.js b/src/core/renderers/webgl/systems/shader/GLShader.js index 1344166..6ab6d8d 100644 --- a/src/core/renderers/webgl/systems/shader/GLShader.js +++ b/src/core/renderers/webgl/systems/shader/GLShader.js @@ -5,6 +5,8 @@ setPrecision = require('./shader/setPrecision'), generateUniformAccessObject = require('./shader/generateUniformAccessObject'); +var ID = 0; + /** * Helper class to create a webGL Shader * @@ -65,6 +67,8 @@ this.uniforms = generateUniformAccessObject( gl, this.uniformData ); this.uniformGroups = {}; + + this.id = ID++; }; /** * Uses this shader diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js index 8849245..ec9cd9c 100644 --- a/src/core/renderers/webgl/systems/ContextSystem.js +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -75,7 +75,8 @@ */ createContext(canvas, options) { - var gl = canvas.getContext('webgl', options) || + var gl = canvas.getContext('webgl2', options) || + canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options); if (!gl) diff --git a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js index 837ba94..0f2138b 100644 --- a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js +++ b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js @@ -22,7 +22,9 @@ { super(renderer); + this._activeGeometry = null; this._activeVao = null; + } /** @@ -43,9 +45,28 @@ */ bind(geometry, glShader) { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + const gl = this.gl; - this.bindVao(vao); + // not sure the best way to address this.. + // currently different shaders require different VAOs for the same geometry + // Still mulling over the best way to solve this one.. + // will likely need to modify the shader attribute locations at run time! + let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + + if(!vaos) + { + vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID] = {}; + } + + const vao = vaos[glShader.id] || this.initGeometryVao(geometry, glShader); + + this._activeGeometry = geometry; + + if(this._activeVao !== vao) + { + this._activeVao = vao; + gl.bindVertexArray(vao); + } // TODO - optimise later! // don't need to loop through if nothing changed! @@ -65,6 +86,21 @@ } } + checkCompatability(geometry, glShader) + { + // geometry must have at least all the attributes that the shader requires. + const geometryAttributes = geometry.attributes; + const shaderAttributes = glShader.attributes; + + for (const j in shaderAttributes) + { + if(!geometryAttributes[j]) + { + throw new Error('shader and geometry incompatible, geometry missing the "' + j + '" attribute'); + } + } + } + /** * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private @@ -73,12 +109,10 @@ */ initGeometryVao(geometry, glShader) { + this.checkCompatability(geometry, glShader); + const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - + const CONTEXT_UID = this.CONTEXT_UID; const buffers = geometry.buffers; const attributes = geometry.attributes; @@ -87,26 +121,20 @@ { const buffer = buffers[i]; - if (!buffer._glBuffers[this.CONTEXT_UID]) + if (!buffer._glBuffers[CONTEXT_UID]) { if (buffer.index) { - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); } else { /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); } } } - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - const tempStride = {}; const tempStart = {}; @@ -118,17 +146,24 @@ for (const j in attributes) { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + if(!attributes[j].size && glShader.attributes[j]) + { + attributes[j].size = glShader.attributes[j].size; + } + + tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type]; } for (const j in attributes) { const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; + const attribSize = attribute.size; + + // must be careful that the geometry has attributes not used by this shader.. if (attribute.stride === undefined) { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) { attribute.stride = 0; } @@ -142,37 +177,95 @@ { attribute.start = tempStart[attribute.buffer]; - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]; } } - // next update the attributes buffer.. + const vao = gl.createVertexArray(); + + gl.bindVertexArray(vao); + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + geometry.indexBuffer._glBuffers[CONTEXT_UID].bind(); + } + + let lastBuffer = null; + + // add a new one! for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; + const glBuffer = buffer._glBuffers[CONTEXT_UID]; - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + if(glShader.attributes[j]) + { + if(lastBuffer !== glBuffer) + { + glBuffer.bind(); + lastBuffer = glBuffer; + } - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); + const location = glShader.attributes[j].location; + + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, + attribute.size, + attribute.type || gl.FLOAT, + attribute.normalized, + attribute.stride, + attribute.start); + + if(attribute.instance) + { + gl.vertexAttribDivisor(location, 1); + } + } } - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + geometry.glVertexArrayObjects[CONTEXT_UID][glShader.id] = vao; + + gl.bindVertexArray(null); return vao; } + draw(type, size, start, instanceCount) { - this._activeVao.draw(type, size, start, instanceCount); + const gl = this.gl; + const geometry = this._activeGeometry; + + //TODO.. this should not change so maybe cache the function? + + if(geometry.indexBuffer) + { + if(geometry.instanced) + { + gl.drawElementsInstanced(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2, instanceCount || 1); + } + else + { + gl.drawElements(type, size || geometry.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2 ); + } + } + else + { + if(geometry.instanced) + { + // TODO need a better way to calculate size.. + gl.drawArrayInstanced(type, start, size || geometry.getSize(), instanceCount || 1); + } + else + { + gl.drawArrays(type, start, size || geometry.getSize()); + } + } + + return this; } /** @@ -193,23 +286,5 @@ */ bindVao(vao) { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; } } diff --git a/src/core/renderers/webgl/systems/shader/GLShader.js b/src/core/renderers/webgl/systems/shader/GLShader.js index 1344166..6ab6d8d 100644 --- a/src/core/renderers/webgl/systems/shader/GLShader.js +++ b/src/core/renderers/webgl/systems/shader/GLShader.js @@ -5,6 +5,8 @@ setPrecision = require('./shader/setPrecision'), generateUniformAccessObject = require('./shader/generateUniformAccessObject'); +var ID = 0; + /** * Helper class to create a webGL Shader * @@ -65,6 +67,8 @@ this.uniforms = generateUniformAccessObject( gl, this.uniformData ); this.uniformGroups = {}; + + this.id = ID++; }; /** * Uses this shader diff --git a/src/core/renderers/webgl/systems/textures/TextureSystem.js b/src/core/renderers/webgl/systems/textures/TextureSystem.js index 342b5fb..b7fef02 100644 --- a/src/core/renderers/webgl/systems/textures/TextureSystem.js +++ b/src/core/renderers/webgl/systems/textures/TextureSystem.js @@ -206,9 +206,8 @@ } } } - if(texture.target === gl.TEXTURE_2D_ARRAY) + else if(texture.target === gl.TEXTURE_2D_ARRAY) { - console.log("REMEMBER THIS IS TOO MANY!") gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, texture.format, @@ -228,12 +227,8 @@ if(texturePart.resource) { - // void gl.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, ImageBitmap? pixels); - console.log(texturePart.resource.source) - if(texturePart.resource.loaded) { - console.log("UPOAD..") gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0,//xoffset @@ -246,12 +241,6 @@ texture.type, texturePart.resource.source); } - - } - else - { - - } } } diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js index 8849245..ec9cd9c 100644 --- a/src/core/renderers/webgl/systems/ContextSystem.js +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -75,7 +75,8 @@ */ createContext(canvas, options) { - var gl = canvas.getContext('webgl', options) || + var gl = canvas.getContext('webgl2', options) || + canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options); if (!gl) diff --git a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js index 837ba94..0f2138b 100644 --- a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js +++ b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js @@ -22,7 +22,9 @@ { super(renderer); + this._activeGeometry = null; this._activeVao = null; + } /** @@ -43,9 +45,28 @@ */ bind(geometry, glShader) { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + const gl = this.gl; - this.bindVao(vao); + // not sure the best way to address this.. + // currently different shaders require different VAOs for the same geometry + // Still mulling over the best way to solve this one.. + // will likely need to modify the shader attribute locations at run time! + let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + + if(!vaos) + { + vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID] = {}; + } + + const vao = vaos[glShader.id] || this.initGeometryVao(geometry, glShader); + + this._activeGeometry = geometry; + + if(this._activeVao !== vao) + { + this._activeVao = vao; + gl.bindVertexArray(vao); + } // TODO - optimise later! // don't need to loop through if nothing changed! @@ -65,6 +86,21 @@ } } + checkCompatability(geometry, glShader) + { + // geometry must have at least all the attributes that the shader requires. + const geometryAttributes = geometry.attributes; + const shaderAttributes = glShader.attributes; + + for (const j in shaderAttributes) + { + if(!geometryAttributes[j]) + { + throw new Error('shader and geometry incompatible, geometry missing the "' + j + '" attribute'); + } + } + } + /** * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private @@ -73,12 +109,10 @@ */ initGeometryVao(geometry, glShader) { + this.checkCompatability(geometry, glShader); + const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - + const CONTEXT_UID = this.CONTEXT_UID; const buffers = geometry.buffers; const attributes = geometry.attributes; @@ -87,26 +121,20 @@ { const buffer = buffers[i]; - if (!buffer._glBuffers[this.CONTEXT_UID]) + if (!buffer._glBuffers[CONTEXT_UID]) { if (buffer.index) { - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); } else { /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); } } } - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - const tempStride = {}; const tempStart = {}; @@ -118,17 +146,24 @@ for (const j in attributes) { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + if(!attributes[j].size && glShader.attributes[j]) + { + attributes[j].size = glShader.attributes[j].size; + } + + tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type]; } for (const j in attributes) { const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; + const attribSize = attribute.size; + + // must be careful that the geometry has attributes not used by this shader.. if (attribute.stride === undefined) { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) { attribute.stride = 0; } @@ -142,37 +177,95 @@ { attribute.start = tempStart[attribute.buffer]; - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]; } } - // next update the attributes buffer.. + const vao = gl.createVertexArray(); + + gl.bindVertexArray(vao); + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + geometry.indexBuffer._glBuffers[CONTEXT_UID].bind(); + } + + let lastBuffer = null; + + // add a new one! for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; + const glBuffer = buffer._glBuffers[CONTEXT_UID]; - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + if(glShader.attributes[j]) + { + if(lastBuffer !== glBuffer) + { + glBuffer.bind(); + lastBuffer = glBuffer; + } - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); + const location = glShader.attributes[j].location; + + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, + attribute.size, + attribute.type || gl.FLOAT, + attribute.normalized, + attribute.stride, + attribute.start); + + if(attribute.instance) + { + gl.vertexAttribDivisor(location, 1); + } + } } - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + geometry.glVertexArrayObjects[CONTEXT_UID][glShader.id] = vao; + + gl.bindVertexArray(null); return vao; } + draw(type, size, start, instanceCount) { - this._activeVao.draw(type, size, start, instanceCount); + const gl = this.gl; + const geometry = this._activeGeometry; + + //TODO.. this should not change so maybe cache the function? + + if(geometry.indexBuffer) + { + if(geometry.instanced) + { + gl.drawElementsInstanced(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2, instanceCount || 1); + } + else + { + gl.drawElements(type, size || geometry.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2 ); + } + } + else + { + if(geometry.instanced) + { + // TODO need a better way to calculate size.. + gl.drawArrayInstanced(type, start, size || geometry.getSize(), instanceCount || 1); + } + else + { + gl.drawArrays(type, start, size || geometry.getSize()); + } + } + + return this; } /** @@ -193,23 +286,5 @@ */ bindVao(vao) { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; } } diff --git a/src/core/renderers/webgl/systems/shader/GLShader.js b/src/core/renderers/webgl/systems/shader/GLShader.js index 1344166..6ab6d8d 100644 --- a/src/core/renderers/webgl/systems/shader/GLShader.js +++ b/src/core/renderers/webgl/systems/shader/GLShader.js @@ -5,6 +5,8 @@ setPrecision = require('./shader/setPrecision'), generateUniformAccessObject = require('./shader/generateUniformAccessObject'); +var ID = 0; + /** * Helper class to create a webGL Shader * @@ -65,6 +67,8 @@ this.uniforms = generateUniformAccessObject( gl, this.uniformData ); this.uniformGroups = {}; + + this.id = ID++; }; /** * Uses this shader diff --git a/src/core/renderers/webgl/systems/textures/TextureSystem.js b/src/core/renderers/webgl/systems/textures/TextureSystem.js index 342b5fb..b7fef02 100644 --- a/src/core/renderers/webgl/systems/textures/TextureSystem.js +++ b/src/core/renderers/webgl/systems/textures/TextureSystem.js @@ -206,9 +206,8 @@ } } } - if(texture.target === gl.TEXTURE_2D_ARRAY) + else if(texture.target === gl.TEXTURE_2D_ARRAY) { - console.log("REMEMBER THIS IS TOO MANY!") gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, texture.format, @@ -228,12 +227,8 @@ if(texturePart.resource) { - // void gl.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, ImageBitmap? pixels); - console.log(texturePart.resource.source) - if(texturePart.resource.loaded) { - console.log("UPOAD..") gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0,//xoffset @@ -246,12 +241,6 @@ texture.type, texturePart.resource.source); } - - } - else - { - - } } } diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js index c773eac..238a4fe 100644 --- a/src/mesh/geometry/Geometry.js +++ b/src/mesh/geometry/Geometry.js @@ -57,8 +57,13 @@ * @type {Array} */ this.glVertexArrayObjects = []; + this.glVertexArrayObjects2 = []; this.id = UID++; + + this.instanced = false; + + this._size = null; } /** @@ -116,6 +121,9 @@ this.attributes[id] = new Attribute(bufferIndex, size, normalised, type, stride, start, instance); + // assuming that if there is instanced data then this will be drawn with instancing! + this.instanced = this.instanced || instance; + this._si return this; } @@ -222,6 +230,20 @@ return this; } + getSize() + { + for (var i in this.attributes) + { + const attribute = this.attributes[i]; + const buffer = this.buffers[attribute.buffer]; + + return buffer.data.length / (( attribute.stride/4 ) || attribute.size); + } + + return 0; + }; + + /** * Destroys the geometry. */ diff --git a/src/core/renderers/webgl/systems/ContextSystem.js b/src/core/renderers/webgl/systems/ContextSystem.js index 8849245..ec9cd9c 100644 --- a/src/core/renderers/webgl/systems/ContextSystem.js +++ b/src/core/renderers/webgl/systems/ContextSystem.js @@ -75,7 +75,8 @@ */ createContext(canvas, options) { - var gl = canvas.getContext('webgl', options) || + var gl = canvas.getContext('webgl2', options) || + canvas.getContext('webgl', options) || canvas.getContext('experimental-webgl', options); if (!gl) diff --git a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js index 837ba94..0f2138b 100644 --- a/src/core/renderers/webgl/systems/geometry/GeometrySystem.js +++ b/src/core/renderers/webgl/systems/geometry/GeometrySystem.js @@ -22,7 +22,9 @@ { super(renderer); + this._activeGeometry = null; this._activeVao = null; + } /** @@ -43,9 +45,28 @@ */ bind(geometry, glShader) { - const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); + const gl = this.gl; - this.bindVao(vao); + // not sure the best way to address this.. + // currently different shaders require different VAOs for the same geometry + // Still mulling over the best way to solve this one.. + // will likely need to modify the shader attribute locations at run time! + let vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID]; + + if(!vaos) + { + vaos = geometry.glVertexArrayObjects[this.CONTEXT_UID] = {}; + } + + const vao = vaos[glShader.id] || this.initGeometryVao(geometry, glShader); + + this._activeGeometry = geometry; + + if(this._activeVao !== vao) + { + this._activeVao = vao; + gl.bindVertexArray(vao); + } // TODO - optimise later! // don't need to loop through if nothing changed! @@ -65,6 +86,21 @@ } } + checkCompatability(geometry, glShader) + { + // geometry must have at least all the attributes that the shader requires. + const geometryAttributes = geometry.attributes; + const shaderAttributes = glShader.attributes; + + for (const j in shaderAttributes) + { + if(!geometryAttributes[j]) + { + throw new Error('shader and geometry incompatible, geometry missing the "' + j + '" attribute'); + } + } + } + /** * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private @@ -73,12 +109,10 @@ */ initGeometryVao(geometry, glShader) { + this.checkCompatability(geometry, glShader); + const gl = this.gl; - - this.bindVao(null); - - const vao = this.createVao(); - + const CONTEXT_UID = this.CONTEXT_UID; const buffers = geometry.buffers; const attributes = geometry.attributes; @@ -87,26 +121,20 @@ { const buffer = buffers[i]; - if (!buffer._glBuffers[this.CONTEXT_UID]) + if (!buffer._glBuffers[CONTEXT_UID]) { if (buffer.index) { - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createIndexBuffer(gl, buffer.data); } else { /* eslint-disable max-len */ - buffer._glBuffers[this.CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); + buffer._glBuffers[CONTEXT_UID] = GLBuffer.createVertexBuffer(gl, buffer.data, buffer.static ? gl.STATIC_DRAW : gl.DYNAMIC_DRAW); } } } - if (geometry.indexBuffer) - { - // first update the index buffer if we have one.. - vao.addIndex(geometry.indexBuffer._glBuffers[this.CONTEXT_UID]); - } - const tempStride = {}; const tempStart = {}; @@ -118,17 +146,24 @@ for (const j in attributes) { - tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; + if(!attributes[j].size && glShader.attributes[j]) + { + attributes[j].size = glShader.attributes[j].size; + } + + tempStride[attributes[j].buffer] += attributes[j].size * byteSizeMap[attributes[j].type]; } for (const j in attributes) { const attribute = attributes[j]; - const glAttribute = glShader.attributes[j]; + const attribSize = attribute.size; + + // must be careful that the geometry has attributes not used by this shader.. if (attribute.stride === undefined) { - if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) + if (tempStride[attribute.buffer] === attribSize * byteSizeMap[attribute.type]) { attribute.stride = 0; } @@ -142,37 +177,95 @@ { attribute.start = tempStart[attribute.buffer]; - tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; + tempStart[attribute.buffer] += attribSize * byteSizeMap[attribute.type]; } } - // next update the attributes buffer.. + const vao = gl.createVertexArray(); + + gl.bindVertexArray(vao); + + if (geometry.indexBuffer) + { + // first update the index buffer if we have one.. + geometry.indexBuffer._glBuffers[CONTEXT_UID].bind(); + } + + let lastBuffer = null; + + // add a new one! for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; + const glBuffer = buffer._glBuffers[CONTEXT_UID]; - const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; + if(glShader.attributes[j]) + { + if(lastBuffer !== glBuffer) + { + glBuffer.bind(); + lastBuffer = glBuffer; + } - // need to know the shader as it means we can be lazy and let pixi do the work for us.. - // stride, start, type? - vao.addAttribute(glBuffer, - glShader.attributes[j], - attribute.type || 5126, // (5126 = FLOAT) - attribute.normalized, - attribute.stride, - attribute.start, - attribute.instance); + const location = glShader.attributes[j].location; + + gl.enableVertexAttribArray(location); + + gl.vertexAttribPointer(location, + attribute.size, + attribute.type || gl.FLOAT, + attribute.normalized, + attribute.stride, + attribute.start); + + if(attribute.instance) + { + gl.vertexAttribDivisor(location, 1); + } + } } - geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; + geometry.glVertexArrayObjects[CONTEXT_UID][glShader.id] = vao; + + gl.bindVertexArray(null); return vao; } + draw(type, size, start, instanceCount) { - this._activeVao.draw(type, size, start, instanceCount); + const gl = this.gl; + const geometry = this._activeGeometry; + + //TODO.. this should not change so maybe cache the function? + + if(geometry.indexBuffer) + { + if(geometry.instanced) + { + gl.drawElementsInstanced(type, size || this.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2, instanceCount || 1); + } + else + { + gl.drawElements(type, size || geometry.indexBuffer.data.length, gl.UNSIGNED_SHORT, (start || 0) * 2 ); + } + } + else + { + if(geometry.instanced) + { + // TODO need a better way to calculate size.. + gl.drawArrayInstanced(type, start, size || geometry.getSize(), instanceCount || 1); + } + else + { + gl.drawArrays(type, start, size || geometry.getSize()); + } + } + + return this; } /** @@ -193,23 +286,5 @@ */ bindVao(vao) { - if (this._activeVao === vao) - { - return this; - } - - if (vao) - { - vao.bind(); - } - else if (this._activeVao) - { - // TODO this should always be true i think? - this._activeVao.unbind(); - } - - this._activeVao = vao; - - return this; } } diff --git a/src/core/renderers/webgl/systems/shader/GLShader.js b/src/core/renderers/webgl/systems/shader/GLShader.js index 1344166..6ab6d8d 100644 --- a/src/core/renderers/webgl/systems/shader/GLShader.js +++ b/src/core/renderers/webgl/systems/shader/GLShader.js @@ -5,6 +5,8 @@ setPrecision = require('./shader/setPrecision'), generateUniformAccessObject = require('./shader/generateUniformAccessObject'); +var ID = 0; + /** * Helper class to create a webGL Shader * @@ -65,6 +67,8 @@ this.uniforms = generateUniformAccessObject( gl, this.uniformData ); this.uniformGroups = {}; + + this.id = ID++; }; /** * Uses this shader diff --git a/src/core/renderers/webgl/systems/textures/TextureSystem.js b/src/core/renderers/webgl/systems/textures/TextureSystem.js index 342b5fb..b7fef02 100644 --- a/src/core/renderers/webgl/systems/textures/TextureSystem.js +++ b/src/core/renderers/webgl/systems/textures/TextureSystem.js @@ -206,9 +206,8 @@ } } } - if(texture.target === gl.TEXTURE_2D_ARRAY) + else if(texture.target === gl.TEXTURE_2D_ARRAY) { - console.log("REMEMBER THIS IS TOO MANY!") gl.texImage3D(gl.TEXTURE_2D_ARRAY, 0, texture.format, @@ -228,12 +227,8 @@ if(texturePart.resource) { - // void gl.texSubImage3D(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, ImageBitmap? pixels); - console.log(texturePart.resource.source) - if(texturePart.resource.loaded) { - console.log("UPOAD..") gl.texSubImage3D(gl.TEXTURE_2D_ARRAY, 0, 0,//xoffset @@ -246,12 +241,6 @@ texture.type, texturePart.resource.source); } - - } - else - { - - } } } diff --git a/src/mesh/geometry/Geometry.js b/src/mesh/geometry/Geometry.js index c773eac..238a4fe 100644 --- a/src/mesh/geometry/Geometry.js +++ b/src/mesh/geometry/Geometry.js @@ -57,8 +57,13 @@ * @type {Array} */ this.glVertexArrayObjects = []; + this.glVertexArrayObjects2 = []; this.id = UID++; + + this.instanced = false; + + this._size = null; } /** @@ -116,6 +121,9 @@ this.attributes[id] = new Attribute(bufferIndex, size, normalised, type, stride, start, instance); + // assuming that if there is instanced data then this will be drawn with instancing! + this.instanced = this.instanced || instance; + this._si return this; } @@ -222,6 +230,20 @@ return this; } + getSize() + { + for (var i in this.attributes) + { + const attribute = this.attributes[i]; + const buffer = this.buffers[attribute.buffer]; + + return buffer.data.length / (( attribute.stride/4 ) || attribute.size); + } + + return 0; + }; + + /** * Destroys the geometry. */ diff --git a/src/mesh/webgl/MeshRenderer.js b/src/mesh/webgl/MeshRenderer.js index ab99fcd..30b85f1 100644 --- a/src/mesh/webgl/MeshRenderer.js +++ b/src/mesh/webgl/MeshRenderer.js @@ -45,14 +45,15 @@ render(mesh) { // bind the shader.. - const glShader = this.renderer.shader.bind(mesh.shader, true); // set the shader props.. - //if (glShader.uniformData.translationMatrix) - //{ + if (mesh.shader.uniforms.translationMatrix) + { // the transform! - // glShader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); - //} + mesh.shader.uniforms.translationMatrix = mesh.transform.worldTransform.toArray(true); + } + + const glShader = this.renderer.shader.bind(mesh.shader, true); // set unifomrs.. this.renderer.shader.syncUniformGroup(mesh.shader.uniformGroup); @@ -63,7 +64,7 @@ // bind the geometry... this.renderer.geometry.bind(mesh.geometry, glShader); // then render it - mesh.geometry.glVertexArrayObjects[this.CONTEXT_UID].draw(mesh.drawMode, mesh.size, mesh.start, mesh.geometry.instanceCount); + this.renderer.geometry.draw(mesh.drawMode, mesh.size, mesh.start, mesh.geometry.instanceCount); }