diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js new file mode 100644 index 0000000..abe4a51 --- /dev/null +++ b/src/core/renderers/webgl/managers/WebGLMaskManager.js @@ -0,0 +1,324 @@ +var WebGLManager = require('./WebGLManager'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function WebGLMaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; +} + +WebGLMaskManager.prototype = Object.create(WebGLManager.prototype); +WebGLMaskManager.prototype.constructor = WebGLMaskManager; +module.exports = WebGLMaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ +WebGLMaskManager.prototype.pushStencil = function (graphics, webGLData) +{ + var gl = this.renderer.gl; + + this.bindGraphics(graphics, webGLData, this.renderer); + + 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! + * + * @param graphics {Graphics} + * @param webGLData {Array} + */ +WebGLMaskManager.prototype.bindGraphics = function (graphics, webGLData) +{ + //if (this._currentGraphics === graphics)return; + this._currentGraphics = graphics; + + var gl = this.renderer.gl; + + // bind the graphics object.. + var projection = this.renderer.projection, + offset = this.renderer.offset, + shader;// = this.renderer.shaderManager.primitiveShader; + + if (webGLData.mode === 1) + { + shader = this.renderer.shaderManager.complexPrimitiveShader; + + this.renderer.shaderManager.setShader(shader); + + gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.uniforms.flipY._location, 1); + + gl.uniform2f(shader.uniforms.projectionVector._location, projection.x, -projection.y); + gl.uniform2f(shader.uniforms.offsetVector._location, -offset.x, -offset.y); + + gl.uniform3fv(shader.uniforms.tint._location, utils.hex2rgb(graphics.tint)); + + gl.uniform3fv(shader.uniforms.color._location, webGLData.color); + + gl.uniform1f(shader.uniforms.alpha._location, graphics.worldAlpha); + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.attributes.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 + { + //this.renderer.shaderManager.activatePrimitiveShader(); + shader = this.renderer.shaderManager.primitiveShader; + this.renderer.shaderManager.setShader( shader ); + + gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); + + gl.uniform1f(shader.uniforms.flipY._location, 1); + + gl.uniform2f(shader.uniforms.projectionVector._location, projection.x, -projection.y); + gl.uniform2f(shader.uniforms.offsetVector._location, -offset.x, -offset.y); + + gl.uniform3fv(shader.uniforms.tint._location, utils.hex2rgb(graphics.tint)); + + gl.uniform1f(shader.uniforms.alpha._location, graphics.worldAlpha); + + + gl.bindBuffer(gl.ARRAY_BUFFER, webGLData.buffer); + + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 4 * 6, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false,4 * 6, 2 * 4); + + // set the index buffer! + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, webGLData.indexBuffer); + } +}; + +/** + * @param graphics {Graphics} + * @param webGLData {Array} + */ +WebGLMaskManager.prototype.popStencil = function (graphics, webGLData) +{ + var gl = this.renderer.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, this.renderer); + + 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. + * + */ +WebGLMaskManager.prototype.destroy = function () +{ + this.renderer = null; + this.stencilStack = null; +}; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ +WebGLMaskManager.prototype.pushMask = function (maskData) +{ + this.renderer.setObjectRenderer(this.renderer.objectRenderers.graphics); + + if (maskData.dirty) + { + this.renderer.objectRenderers.graphics.updateGraphics(maskData, this.renderer.gl); + } + + if (!maskData._webGL[this.renderer.gl.id].data.length) + { + return; + } + + this.pushStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +WebGLMaskManager.prototype.popMask = function (maskData) +{ + this.renderer.setObjectRenderer(this.renderer.objectRenderers.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; +