/** * @author Mat Groves http://matgroves.com/ @Doormat23 */ /** * @class WebGLStencilManager * @constructor * @private */ PIXI.WebGLStencilManager = function() { this.stencilStack = []; this.reverse = true; this.count = 0; }; /** * Sets the drawing context to the one given in parameter. * * @method setContext * @param gl {WebGLContext} the current WebGL drawing context */ PIXI.WebGLStencilManager.prototype.setContext = function(gl) { this.gl = gl; }; /** * Applies the Mask and adds it to the current filter stack. * * @method pushMask * @param graphics {Graphics} * @param webGLData {Array} * @param renderSession {Object} */ PIXI.WebGLStencilManager.prototype.pushStencil = function(graphics, webGLData, renderSession) { var gl = this.gl; this.bindGraphics(graphics, webGLData, renderSession); if(this.stencilStack.length === 0) { gl.enable(gl.STENCIL_TEST); gl.clear(gl.STENCIL_BUFFER_BIT); this.reverse = true; this.count = 0; } this.stencilStack.push(webGLData); var level = this.count; gl.colorMask(false, false, false, false); gl.stencilFunc(gl.ALWAYS,0,0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); // draw the triangle strip! if(webGLData.mode === 1) { gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); if(this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } // draw a quad to increment.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); if(this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); } this.reverse = !this.reverse; } else { if(!this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); if(!this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level+1), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); } } gl.colorMask(true, true, true, true); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); this.count++; }; /** * TODO this does not belong here! * * @method bindGraphics * @param graphics {Graphics} * @param webGLData {Array} * @param renderSession {Object} */ PIXI.WebGLStencilManager.prototype.bindGraphics = function(graphics, webGLData, renderSession) { //if(this._currentGraphics === graphics)return; this._currentGraphics = graphics; var gl = this.gl; // bind the graphics object.. var projection = renderSession.projection, offset = renderSession.offset, shader;// = renderSession.shaderManager.primitiveShader; if(webGLData.mode === 1) { shader = renderSession.shaderManager.complexPrimitiveShader; renderSession.shaderManager.setShader( shader ); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); gl.uniform3fv(shader.color, webGLData.color); gl.uniform1f(shader.alpha, graphics.worldAlpha * webGLData.alpha); gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 2, 0); // now do the rest.. // set the index buffer! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); } else { //renderSession.shaderManager.activatePrimitiveShader(); shader = renderSession.shaderManager.primitiveShader; renderSession.shaderManager.setShader( shader ); gl.uniformMatrix3fv(shader.translationMatrix, false, graphics.worldTransform.toArray(true)); gl.uniform2f(shader.projectionVector, projection.x, -projection.y); gl.uniform2f(shader.offsetVector, -offset.x, -offset.y); gl.uniform3fv(shader.tintColor, PIXI.hex2rgb(graphics.tint)); gl.uniform1f(shader.alpha, graphics.worldAlpha); gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false,4 * 6, 2 * 4); // set the index buffer! gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); } }; /** * @method popStencil * @param graphics {Graphics} * @param webGLData {Array} * @param renderSession {Object} */ PIXI.WebGLStencilManager.prototype.popStencil = function(graphics, webGLData, renderSession) { var gl = this.gl; this.stencilStack.pop(); this.count--; if(this.stencilStack.length === 0) { // the stack is empty! gl.disable(gl.STENCIL_TEST); } else { var level = this.count; this.bindGraphics(graphics, webGLData, renderSession); gl.colorMask(false, false, false, false); if(webGLData.mode === 1) { this.reverse = !this.reverse; if(this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } // draw a quad to increment.. gl.drawElements(gl.TRIANGLE_FAN, 4, gl.UNSIGNED_SHORT, ( webGLData.indices.length - 4 ) * 2 ); gl.stencilFunc(gl.ALWAYS,0,0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INVERT); // draw the triangle strip! gl.drawElements(gl.TRIANGLE_FAN, webGLData.indices.length - 4, gl.UNSIGNED_SHORT, 0 ); if(!this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); } } else { // console.log("<<>>") if(!this.reverse) { gl.stencilFunc(gl.EQUAL, 0xFF - (level+1), 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.INCR); } else { gl.stencilFunc(gl.EQUAL,level+1, 0xFF); gl.stencilOp(gl.KEEP,gl.KEEP,gl.DECR); } gl.drawElements(gl.TRIANGLE_STRIP, webGLData.indices.length, gl.UNSIGNED_SHORT, 0 ); if(!this.reverse) { gl.stencilFunc(gl.EQUAL,0xFF-(level), 0xFF); } else { gl.stencilFunc(gl.EQUAL,level, 0xFF); } } gl.colorMask(true, true, true, true); gl.stencilOp(gl.KEEP,gl.KEEP,gl.KEEP); } }; /** * Destroys the mask stack. * * @method destroy */ PIXI.WebGLStencilManager.prototype.destroy = function() { this.stencilStack = null; this.gl = null; };