import WebGLSystem from './WebGLSystem'; import { Rectangle, Matrix } from '../../../math'; import glCore from 'pixi-gl-core'; const byteSizeMap = { 5126: 4, 5123: 2, 5121: 1 }; /** * @class * @extends PIXI.WebGLSystem * @memberof PIXI */ export default class GeometrySystem extends WebGLSystem { /** * @param {PIXI.WebGLRenderer} renderer - The renderer this System works for. */ constructor(renderer) { super(renderer); this._activeVao = null; } /** * Sets up the renderer context and necessary buffers. * * @private */ contextChange() { this.gl = this.renderer.gl; this.CONTEXT_UID = this.renderer.CONTEXT_UID; } /** * Binds geometry so that is can be drawn. Creating a Vao if required * @private * @param {PIXI.mesh.Geometry} geometry instance of geometry to bind * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. */ bind(geometry, glShader) { const vao = geometry.glVertexArrayObjects[this.CONTEXT_UID] || this.initGeometryVao(geometry, glShader); this.bindVao(vao); // TODO - optimise later! // don't need to loop through if nothing changed! // maybe look to add an 'autoupdate' to geometry? for (let i = 0; i < geometry.buffers.length; i++) { const buffer = geometry.buffers[i]; const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; if (buffer._updateID !== glBuffer._updateID) { glBuffer._updateID = buffer._updateID; // TODO - partial upload?? glBuffer.upload(buffer.data, 0); } } } /** * Creates a Vao with the same structure as the geometry and stores it on the geometry. * @private * @param {PIXI.mesh.Geometry} geometry instance of geometry to to generate Vao for * @param {PIXI.glCore.glShader} glShader shader that the geometry will be renderered with. * @return {PIXI.glCore.VertexArrayObject} Returns a fresh vao. */ initGeometryVao(geometry, glShader) { const gl = this.gl; this.bindVao(null); const vao = this.createVao(); const buffers = geometry.buffers; const attributes = geometry.attributes; // first update - and create the buffers! for (let i = 0; i < buffers.length; i++) { const buffer = buffers[i]; if (!buffer._glBuffers[this.CONTEXT_UID]) { if (buffer.index) { buffer._glBuffers[this.CONTEXT_UID] = glCore.GLBuffer.createIndexBuffer(gl, buffer.data); } else { /* eslint-disable max-len */ buffer._glBuffers[this.CONTEXT_UID] = glCore.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 = {}; for (const j in buffers) { tempStride[j] = 0; tempStart[j] = 0; } for (const j in attributes) { tempStride[attributes[j].buffer] += glShader.attributes[j].size * byteSizeMap[attributes[j].type]; } for (const j in attributes) { const attribute = attributes[j]; const glAttribute = glShader.attributes[j]; if (attribute.stride === undefined) { if (tempStride[attribute.buffer] === glAttribute.size * byteSizeMap[attribute.type]) { attribute.stride = 0; } else { attribute.stride = tempStride[attribute.buffer]; } } if (attribute.start === undefined) { attribute.start = tempStart[attribute.buffer]; tempStart[attribute.buffer] += glAttribute.size * byteSizeMap[attribute.type]; } } // next update the attributes buffer.. for (const j in attributes) { const attribute = attributes[j]; const buffer = buffers[attribute.buffer]; const glBuffer = buffer._glBuffers[this.CONTEXT_UID]; // 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); } geometry.glVertexArrayObjects[this.CONTEXT_UID] = vao; return vao; } draw(type, size, start, instanceCount) { this._activeVao.draw(type, size, start, instanceCount); } /** * Creates a new VAO from this renderer's context and state. * * @return {VertexArrayObject} The new VAO. */ createVao() { return new glCore.VertexArrayObject(this.gl, this.renderer.state.attribState); } /** * Changes the current Vao to the one given in parameter * * @param {PIXI.VertexArrayObject} vao - the new Vao * @return {PIXI.WebGLRenderer} Returns itself. */ 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; } }