Phaser Source: src/pixi/renderers/webgl/utils/WebGLStencilManager.js

# Source: src/pixi/renderers/webgl/utils/WebGLStencilManager.js

    /**
     * @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.uniform1f(shader.flipY, renderSession.flipY);

            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.uniform1f(shader.flipY, renderSession.flipY);
            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;
    };