diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/extras/StripShader.js b/src/extras/StripShader.js index 3e7c47f..c7b69b3 100644 --- a/src/extras/StripShader.js +++ b/src/extras/StripShader.js @@ -53,4 +53,4 @@ StripShader.prototype.constructor = StripShader; module.exports = StripShader; -core.WebGLShaderManager.registerPlugin('stripShader', StripShader); +//core.WebGLShaderManager.registerPlugin('stripShader', StripShader); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/extras/StripShader.js b/src/extras/StripShader.js index 3e7c47f..c7b69b3 100644 --- a/src/extras/StripShader.js +++ b/src/extras/StripShader.js @@ -53,4 +53,4 @@ StripShader.prototype.constructor = StripShader; module.exports = StripShader; -core.WebGLShaderManager.registerPlugin('stripShader', StripShader); +//core.WebGLShaderManager.registerPlugin('stripShader', StripShader); diff --git a/src/filters/AbstractFilter.js b/src/filters/AbstractFilter.js deleted file mode 100644 index a214f82..0000000 --- a/src/filters/AbstractFilter.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. - * If you want to make a custom filter this should be your base class. - * - * @class - * @namespace PIXI - * @param fragmentSrc {string|string[]} The fragment source in an array of strings. - * @param uniforms {object} An object containing the uniforms for this filter. - */ -function AbstractFilter(fragmentSrc, uniforms) -{ - /** - * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. - * For example the blur filter has two passes blurX and blurY. - * - * @member {AbstractFilter[]} - * @private - */ - this.passes = [this]; - - /** - * @member {Shader[]} - * @private - */ - this.shaders = []; - - /** - * @member {number} - */ - this.padding = 0; - - /** - * @member {object} - * @private - */ - this.uniforms = uniforms || {}; - - /** - * @member {string[]} - * @private - */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); -} - -AbstractFilter.prototype.constructor = AbstractFilter; -module.exports = AbstractFilter; - -/** - * Syncs the uniforms between the class object and the shaders. - * - */ -AbstractFilter.prototype.syncUniforms = function () -{ - for (var i = 0, j = this.shaders.length; i < j; ++i) - { - this.shaders[i].dirty = true; - } -}; - -/* -AbstractFilter.prototype.apply = function (frameBuffer) -{ - // TODO :) -}; -*/ diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/extras/StripShader.js b/src/extras/StripShader.js index 3e7c47f..c7b69b3 100644 --- a/src/extras/StripShader.js +++ b/src/extras/StripShader.js @@ -53,4 +53,4 @@ StripShader.prototype.constructor = StripShader; module.exports = StripShader; -core.WebGLShaderManager.registerPlugin('stripShader', StripShader); +//core.WebGLShaderManager.registerPlugin('stripShader', StripShader); diff --git a/src/filters/AbstractFilter.js b/src/filters/AbstractFilter.js deleted file mode 100644 index a214f82..0000000 --- a/src/filters/AbstractFilter.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. - * If you want to make a custom filter this should be your base class. - * - * @class - * @namespace PIXI - * @param fragmentSrc {string|string[]} The fragment source in an array of strings. - * @param uniforms {object} An object containing the uniforms for this filter. - */ -function AbstractFilter(fragmentSrc, uniforms) -{ - /** - * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. - * For example the blur filter has two passes blurX and blurY. - * - * @member {AbstractFilter[]} - * @private - */ - this.passes = [this]; - - /** - * @member {Shader[]} - * @private - */ - this.shaders = []; - - /** - * @member {number} - */ - this.padding = 0; - - /** - * @member {object} - * @private - */ - this.uniforms = uniforms || {}; - - /** - * @member {string[]} - * @private - */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); -} - -AbstractFilter.prototype.constructor = AbstractFilter; -module.exports = AbstractFilter; - -/** - * Syncs the uniforms between the class object and the shaders. - * - */ -AbstractFilter.prototype.syncUniforms = function () -{ - for (var i = 0, j = this.shaders.length; i < j; ++i) - { - this.shaders[i].dirty = true; - } -}; - -/* -AbstractFilter.prototype.apply = function (frameBuffer) -{ - // TODO :) -}; -*/ diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js index 97f60c3..0426b94 100644 --- a/src/filters/AlphaMaskFilter.js +++ b/src/filters/AlphaMaskFilter.js @@ -10,18 +10,24 @@ * @namespace PIXI * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment */ -function AlphaMaskFilter(texture) +function AlphaMaskFilter(sprite) { AbstractFilter.call(this); - texture.baseTexture._powerOf2 = true; + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; // set the uniforms this.uniforms = { mask: { type: 'sampler2D', value: texture }, mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, dimensions: { type: '4fv', value: [0, 0, 0, 0] }, - offset: { type: '2f', value: { x: 0, y: 0 } } + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } }; if (texture.baseTexture.hasLoaded) @@ -36,9 +42,38 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + this.fragmentSrc = [ 'precision mediump float;', + 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -50,20 +85,23 @@ 'void main()', '{', - ' vec2 mapCords = vTextureCoord.xy;', + /* ' vec2 mapCords = vTextureCoord.xy;', ' mapCords += (dimensions.zw + offset)/ dimensions.xy ;', ' mapCords.y *= -1.0;', ' mapCords.y += 1.0;', ' mapCords *= dimensions.xy / mapDimensions;', + ' float maskAlpha = texture2D(mask, mapCords).r;',*/ ' vec4 original = texture2D(uSampler, vTextureCoord);', - ' float maskAlpha = texture2D(mask, mapCords).r;', - ' original *= maskAlpha;', + + ' vec4 masky = texture2D(mask, vMaskCoord);', + + ' original *= (masky.r * masky.a);', //' original.rgb *= maskAlpha;', ' gl_FragColor = original;', - //' gl_FragColor = gl_FragColor;', + //' gl_FragColor = masky;', '}' - ]; + ].join('\n'); } AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/extras/StripShader.js b/src/extras/StripShader.js index 3e7c47f..c7b69b3 100644 --- a/src/extras/StripShader.js +++ b/src/extras/StripShader.js @@ -53,4 +53,4 @@ StripShader.prototype.constructor = StripShader; module.exports = StripShader; -core.WebGLShaderManager.registerPlugin('stripShader', StripShader); +//core.WebGLShaderManager.registerPlugin('stripShader', StripShader); diff --git a/src/filters/AbstractFilter.js b/src/filters/AbstractFilter.js deleted file mode 100644 index a214f82..0000000 --- a/src/filters/AbstractFilter.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. - * If you want to make a custom filter this should be your base class. - * - * @class - * @namespace PIXI - * @param fragmentSrc {string|string[]} The fragment source in an array of strings. - * @param uniforms {object} An object containing the uniforms for this filter. - */ -function AbstractFilter(fragmentSrc, uniforms) -{ - /** - * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. - * For example the blur filter has two passes blurX and blurY. - * - * @member {AbstractFilter[]} - * @private - */ - this.passes = [this]; - - /** - * @member {Shader[]} - * @private - */ - this.shaders = []; - - /** - * @member {number} - */ - this.padding = 0; - - /** - * @member {object} - * @private - */ - this.uniforms = uniforms || {}; - - /** - * @member {string[]} - * @private - */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); -} - -AbstractFilter.prototype.constructor = AbstractFilter; -module.exports = AbstractFilter; - -/** - * Syncs the uniforms between the class object and the shaders. - * - */ -AbstractFilter.prototype.syncUniforms = function () -{ - for (var i = 0, j = this.shaders.length; i < j; ++i) - { - this.shaders[i].dirty = true; - } -}; - -/* -AbstractFilter.prototype.apply = function (frameBuffer) -{ - // TODO :) -}; -*/ diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js index 97f60c3..0426b94 100644 --- a/src/filters/AlphaMaskFilter.js +++ b/src/filters/AlphaMaskFilter.js @@ -10,18 +10,24 @@ * @namespace PIXI * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment */ -function AlphaMaskFilter(texture) +function AlphaMaskFilter(sprite) { AbstractFilter.call(this); - texture.baseTexture._powerOf2 = true; + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; // set the uniforms this.uniforms = { mask: { type: 'sampler2D', value: texture }, mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, dimensions: { type: '4fv', value: [0, 0, 0, 0] }, - offset: { type: '2f', value: { x: 0, y: 0 } } + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } }; if (texture.baseTexture.hasLoaded) @@ -36,9 +42,38 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + this.fragmentSrc = [ 'precision mediump float;', + 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -50,20 +85,23 @@ 'void main()', '{', - ' vec2 mapCords = vTextureCoord.xy;', + /* ' vec2 mapCords = vTextureCoord.xy;', ' mapCords += (dimensions.zw + offset)/ dimensions.xy ;', ' mapCords.y *= -1.0;', ' mapCords.y += 1.0;', ' mapCords *= dimensions.xy / mapDimensions;', + ' float maskAlpha = texture2D(mask, mapCords).r;',*/ ' vec4 original = texture2D(uSampler, vTextureCoord);', - ' float maskAlpha = texture2D(mask, mapCords).r;', - ' original *= maskAlpha;', + + ' vec4 masky = texture2D(mask, vMaskCoord);', + + ' original *= (masky.r * masky.a);', //' original.rgb *= maskAlpha;', ' gl_FragColor = original;', - //' gl_FragColor = gl_FragColor;', + //' gl_FragColor = masky;', '}' - ]; + ].join('\n'); } AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/ColorMatrixFilter.js b/src/filters/ColorMatrixFilter.js index 6750c3b..462dea1 100644 --- a/src/filters/ColorMatrixFilter.js +++ b/src/filters/ColorMatrixFilter.js @@ -33,7 +33,8 @@ 'void main(void)', '{', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * 2.0;', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * 1.0;', + // ' gl_FragColor *= gl_FragCoord.x;', // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//texture2D(uSampler, vTextureCoord) * matrix;', '}' ].join('\n'); diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/extras/StripShader.js b/src/extras/StripShader.js index 3e7c47f..c7b69b3 100644 --- a/src/extras/StripShader.js +++ b/src/extras/StripShader.js @@ -53,4 +53,4 @@ StripShader.prototype.constructor = StripShader; module.exports = StripShader; -core.WebGLShaderManager.registerPlugin('stripShader', StripShader); +//core.WebGLShaderManager.registerPlugin('stripShader', StripShader); diff --git a/src/filters/AbstractFilter.js b/src/filters/AbstractFilter.js deleted file mode 100644 index a214f82..0000000 --- a/src/filters/AbstractFilter.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. - * If you want to make a custom filter this should be your base class. - * - * @class - * @namespace PIXI - * @param fragmentSrc {string|string[]} The fragment source in an array of strings. - * @param uniforms {object} An object containing the uniforms for this filter. - */ -function AbstractFilter(fragmentSrc, uniforms) -{ - /** - * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. - * For example the blur filter has two passes blurX and blurY. - * - * @member {AbstractFilter[]} - * @private - */ - this.passes = [this]; - - /** - * @member {Shader[]} - * @private - */ - this.shaders = []; - - /** - * @member {number} - */ - this.padding = 0; - - /** - * @member {object} - * @private - */ - this.uniforms = uniforms || {}; - - /** - * @member {string[]} - * @private - */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); -} - -AbstractFilter.prototype.constructor = AbstractFilter; -module.exports = AbstractFilter; - -/** - * Syncs the uniforms between the class object and the shaders. - * - */ -AbstractFilter.prototype.syncUniforms = function () -{ - for (var i = 0, j = this.shaders.length; i < j; ++i) - { - this.shaders[i].dirty = true; - } -}; - -/* -AbstractFilter.prototype.apply = function (frameBuffer) -{ - // TODO :) -}; -*/ diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js index 97f60c3..0426b94 100644 --- a/src/filters/AlphaMaskFilter.js +++ b/src/filters/AlphaMaskFilter.js @@ -10,18 +10,24 @@ * @namespace PIXI * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment */ -function AlphaMaskFilter(texture) +function AlphaMaskFilter(sprite) { AbstractFilter.call(this); - texture.baseTexture._powerOf2 = true; + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; // set the uniforms this.uniforms = { mask: { type: 'sampler2D', value: texture }, mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, dimensions: { type: '4fv', value: [0, 0, 0, 0] }, - offset: { type: '2f', value: { x: 0, y: 0 } } + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } }; if (texture.baseTexture.hasLoaded) @@ -36,9 +42,38 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + this.fragmentSrc = [ 'precision mediump float;', + 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -50,20 +85,23 @@ 'void main()', '{', - ' vec2 mapCords = vTextureCoord.xy;', + /* ' vec2 mapCords = vTextureCoord.xy;', ' mapCords += (dimensions.zw + offset)/ dimensions.xy ;', ' mapCords.y *= -1.0;', ' mapCords.y += 1.0;', ' mapCords *= dimensions.xy / mapDimensions;', + ' float maskAlpha = texture2D(mask, mapCords).r;',*/ ' vec4 original = texture2D(uSampler, vTextureCoord);', - ' float maskAlpha = texture2D(mask, mapCords).r;', - ' original *= maskAlpha;', + + ' vec4 masky = texture2D(mask, vMaskCoord);', + + ' original *= (masky.r * masky.a);', //' original.rgb *= maskAlpha;', ' gl_FragColor = original;', - //' gl_FragColor = gl_FragColor;', + //' gl_FragColor = masky;', '}' - ]; + ].join('\n'); } AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/ColorMatrixFilter.js b/src/filters/ColorMatrixFilter.js index 6750c3b..462dea1 100644 --- a/src/filters/ColorMatrixFilter.js +++ b/src/filters/ColorMatrixFilter.js @@ -33,7 +33,8 @@ 'void main(void)', '{', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * 2.0;', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * 1.0;', + // ' gl_FragColor *= gl_FragCoord.x;', // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//texture2D(uSampler, vTextureCoord) * matrix;', '}' ].join('\n'); diff --git a/src/filters/index.js b/src/filters/index.js index eba8fcb..c65a115 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -9,7 +9,6 @@ * @namespace PIXI */ module.exports = { - AbstractFilter: require('./AbstractFilter'), AlphaMaskFilter: require('./AlphaMaskFilter'), AsciiFilter: require('./AsciiFilter'), BlurFilter: require('./BlurFilter'), diff --git a/src/core/display/DisplayObject.js b/src/core/display/DisplayObject.js index c5f3de6..3ffd6d5 100644 --- a/src/core/display/DisplayObject.js +++ b/src/core/display/DisplayObject.js @@ -24,13 +24,6 @@ this.scale = new math.Point(1, 1); /** - * The skew of the object. - * - * @member {Point} - */ - this.skew = new math.Point(0, 0); - - /** * The pivot point of the displayObject that it rotates around * * @member {Point} @@ -105,8 +98,7 @@ * @member {number} * @private */ - this._srB = 0; - this._srC = 0; + this._sr = 0; /** * cached cos rotation @@ -114,8 +106,7 @@ * @member {number} * @private */ - this._crA = 1; - this._crD = 1; + this._cr = 1; /** * The original, cached bounds of the object @@ -141,21 +132,15 @@ */ this._mask = null; - /** - * Used internally to optimize updateTransform. - * - * @member {number} - * @private - */ - this._cachedRotX = 0; + this._isMask = false; /** - * Used internally to optimize updateTransform. + * Cached internal flag. * - * @member {number} + * @member {boolean} * @private */ - this._cachedRotY = 0; + this._cacheIsDirty = false; } // constructor @@ -262,32 +247,14 @@ filters: { get: function () { - return this._filters; + return this._filters.slice(); }, set: function (value) { - this._filters = value; - /* - if (value) - { - // now put all the passes in one place.. - var passes = []; + this._filters = value.slice(); - for (var i = 0; i < value.length; i++) - { - var filterPasses = value[i].passes; + //if(this._mask) - for (var j = 0; j < filterPasses.length; j++) - { - passes.push(filterPasses[j]); - } - } - - // TODO change this as it is legacy - this._filterBlock = { target: this, filterPasses: passes }; - } - - this._filters = value;*/ } } }); @@ -307,35 +274,26 @@ var wt = this.worldTransform; // temporary matrix variables - var a, b, c, d, tx, ty, - rotY = this.rotation + this.skew.y, - rotX = this.rotation + this.skew.x; + var a, b, c, d, tx, ty; // so if rotation is between 0 then we can simplify the multiplication process.. - if (rotY % math.PI_2 || rotX % math.PI_2) + if (this.rotation % math.PI_2) { // check to see if the rotation is the same as the previous render. This means we only need to use sin and cos when rotation actually changes - if (rotX !== this._cachedRotX || rotY !== this._cachedRotY) + if (this.rotation !== this.rotationCache) { - // cache new values - this._cachedRotX = rotX; - this._cachedRotY = rotY; - - // recalculate expensive ops - this._crA = Math.cos(rotY); - this._srB = Math.sin(rotY); - - this._srC = Math.sin(-rotX); - this._crD = Math.cos(rotX); + this.rotationCache = this.rotation; + this._sr = Math.sin(this.rotation); + this._cr = Math.cos(this.rotation); } // get the matrix values of the displayobject based on its transform properties.. - a = this._crA * this.scale.x; - b = this._srB * this.scale.x; - c = this._srC * this.scale.y; - d = this._crD * this.scale.y; - tx = this.position.x; - ty = this.position.y; + a = this._cr * this.scale.x; + b = this._sr * this.scale.x; + c = -this._sr * this.scale.y; + d = this._cr * this.scale.y; + tx = this.position.x; + ty = this.position.y; // check for pivot.. not often used so geared towards that fact! if (this.pivot.x || this.pivot.y) diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 4af3f82..146e5ba 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -481,7 +481,7 @@ DisplayObjectContainer.prototype.renderWebGL = function (renderer) { // if the object is not visible or the alpha is 0 then no need to render this element - if (!this.visible || this.alpha <= 0) + if (this.isMask || !this.visible || this.alpha <= 0) { return; } @@ -497,19 +497,22 @@ // do a quick check to see if this element has a mask or a filter. if (this._mask || this._filters) { + renderer.currentRenderer.flush(); + // push filter first as we need to ensure the stencil buffer is correct for any masking if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.pushFilter(this, this._filters); - renderer.currentRenderer.start(); } if (this._mask) { - renderer.maskManager.pushMask(this.mask, renderer); + renderer.maskManager.pushMask(this, this._mask); } + renderer.currentRenderer.start(); + + // add this object to the batch, only rendered if it has a texture. this._renderWebGL(renderer); @@ -519,18 +522,19 @@ this.children[i].renderWebGL(renderer); } + renderer.currentRenderer.flush(); if (this._mask) { - renderer.maskManager.popMask(this._mask, renderer); + renderer.maskManager.popMask(this, this._mask); } if (this._filters) { - renderer.currentRenderer.flush(); renderer.filterManager.popFilter(); - renderer.currentRenderer.start(); + } + renderer.currentRenderer.start(); } else diff --git a/src/core/graphics/webgl/ComplexPrimitiveShader.js b/src/core/graphics/webgl/ComplexPrimitiveShader.js deleted file mode 100644 index 1ca0c24..0000000 --- a/src/core/graphics/webgl/ComplexPrimitiveShader.js +++ /dev/null @@ -1,56 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function ComplexPrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform vec3 tint;', - 'uniform float alpha;', - 'uniform vec3 color;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - color: { type: '3f', value: [0,0,0] }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - } - ); -} - -ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); -ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; -module.exports = ComplexPrimitiveShader; - -WebGLShaderManager.registerPlugin('complexPrimitiveShader', ComplexPrimitiveShader); diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index a63640e..977ba79 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -3,9 +3,7 @@ CONST = require('../../const'), ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - WebGLGraphicsData = require('./WebGLGraphicsData'), - ComplexPrimitiveShader = require('./ComplexPrimitiveShader'), - PrimitiveShader = require('./PrimitiveShader'); + WebGLGraphicsData = require('./WebGLGraphicsData'); /** * Renders the graphics object. @@ -20,6 +18,9 @@ ObjectRenderer.call(this, renderer); this.graphicsDataPool = []; + + this.primitiveShader = null; + this.complexPrimitiveShader = null; } GraphicsRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -28,6 +29,11 @@ WebGLRenderer.registerPlugin('graphics', GraphicsRenderer); +GraphicsRenderer.prototype.onContextChange = function() +{ + +} + /** * Destroys this renderer. * @@ -84,8 +90,9 @@ webGLData = webGL.data[i]; + shader = renderer.shaderManager.primitiveShader; + renderer.shaderManager.setShader( shader );//activatePrimitiveShader(); - shader = renderer.shaderManager.plugins.primitiveShader; gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); diff --git a/src/core/graphics/webgl/PrimitiveShader.js b/src/core/graphics/webgl/PrimitiveShader.js deleted file mode 100644 index b7c5766..0000000 --- a/src/core/graphics/webgl/PrimitiveShader.js +++ /dev/null @@ -1,58 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLShaderManager = require('../../renderers/webgl/managers/WebGLShaderManager'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function PrimitiveShader(shaderManager) -{ - Shader.call(this, - shaderManager, - // vertex shader - [ - 'attribute vec2 aVertexPosition;', - 'attribute vec4 aColor;', - - 'uniform mat3 translationMatrix;', - 'uniform mat3 projectionMatrix;', - - 'uniform float alpha;', - 'uniform float flipY;', - 'uniform vec3 tint;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', - ' vColor = aColor * vec4(tint * alpha, alpha);', - '}' - ].join('\n'), - // fragment shader - [ - 'precision mediump float;', - - 'varying vec4 vColor;', - - 'void main(void){', - ' gl_FragColor = vColor;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aColor:0 - } - ); -} - -PrimitiveShader.prototype = Object.create(Shader.prototype); -PrimitiveShader.prototype.constructor = PrimitiveShader; -module.exports = PrimitiveShader; - -WebGLShaderManager.registerPlugin('primitiveShader', PrimitiveShader); diff --git a/src/core/math/Matrix.js b/src/core/math/Matrix.js index 81de05c..e66777b 100644 --- a/src/core/math/Matrix.js +++ b/src/core/math/Matrix.js @@ -240,6 +240,53 @@ }; /** + * Prepends the given Matrix to this Matrix. + * + * @param {Matrix} matrix + * @return {Matrix} This matrix. Good for chaining method calls. + */ +Matrix.prototype.prepend = function(matrix) +{ + var tx1 = this.tx; + + if (matrix.a != 1 || matrix.b != 0 || matrix.c != 0 || matrix.d != 1) + { + var a1 = this.a; + var c1 = this.c; + this.a = a1*matrix.a+this.b*matrix.c; + this.b = a1*matrix.b+this.b*matrix.d; + this.c = c1*matrix.a+this.d*matrix.c; + this.d = c1*matrix.b+this.d*matrix.d; + } + + this.tx = tx1*matrix.a+this.ty*matrix.c+matrix.tx; + this.ty = tx1*matrix.b+this.ty*matrix.d+matrix.ty; + + return this; +}; + + +Matrix.prototype.invert = function() +{ + var a1 = this.a; + var b1 = this.b; + var c1 = this.c; + var d1 = this.d; + var tx1 = this.tx; + var n = a1*d1-b1*c1; + + this.a = d1/n; + this.b = -b1/n; + this.c = -c1/n; + this.d = a1/n; + this.tx = (c1*this.ty-d1*tx1)/n; + this.ty = -(a1*this.ty-b1*tx1)/n; + + return this; +}; + + +/** * Resets this Matix to an identity (default) matrix. * * @return {Matrix} This matrix. Good for chaining method calls. @@ -270,4 +317,18 @@ return matrix; }; +Matrix.prototype.copy = function (matrix) +{ + matrix.a = this.a; + matrix.b = this.b; + matrix.c = this.c; + matrix.d = this.d; + matrix.tx = this.tx; + matrix.ty = this.ty; + + return matrix; +}; + + Matrix.IDENTITY = new Matrix(); +Matrix.TEMP_MATRIX = new Matrix(); diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 8ff6208..aca919c 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -1,5 +1,6 @@ var WebGLShaderManager = require('./managers/WebGLShaderManager'), - WebGLMaskManager = require('./managers/WebGLMaskManager'), + MaskManager = require('./managers/MaskManager'), + StencilManager = require('./managers/StencilManager'), WebGLFilterManager = require('./managers/WebGLFilterManager'), WebGLBlendModeManager = require('./managers/WebGLBlendModeManager'), RenderTarget = require('./utils/RenderTarget'), @@ -173,8 +174,7 @@ // time to create the render managers! each one focuses on managing a state in webGL - // initialize the context so it is ready for the managers. - this._initContext(); + /** * Deals with managing the shader programs and their attribs @@ -184,9 +184,11 @@ /** * Manages the masks using the stencil buffer - * @member {WebGLMaskManager} + * @member {MaskManager} */ - this.maskManager = new WebGLMaskManager(this); + this.maskManager = new MaskManager(this); + + this.stencilManager = new StencilManager(this); /** * Manages the filters @@ -194,8 +196,6 @@ */ this.filterManager = new WebGLFilterManager(this); - //TODO FIX THIS EVENT>> - this.filterManager.initShaderBuffers(); /** * Manages the blendModes @@ -205,15 +205,14 @@ this.blendModes = null; + + this._boundUpdateTexture = this.updateTexture.bind(this); this._boundDestroyTexture = this.destroyTexture.bind(this); this.currentRenderTarget = this.renderTarget; - // map some webGL blend modes.. - this._mapBlendModes(); - /** * This temporary display object used as the parent of the currently being rendered item * @member DisplayObject @@ -221,9 +220,15 @@ */ this._tempDisplayObjectParent = {worldTransform:new math.Matrix(), worldAlpha:1}; - this.currentRenderer = new ObjectRenderer(); + this.currentRenderer = new ObjectRenderer(this); this.initPlugins(); + + // initialize the context so it is ready for the managers. + this._initContext(); + + // map some webGL blend modes.. + this._mapBlendModes(); } // constructor diff --git a/src/core/renderers/webgl/managers/MaskManager.js b/src/core/renderers/webgl/managers/MaskManager.js new file mode 100644 index 0000000..66c54e9 --- /dev/null +++ b/src/core/renderers/webgl/managers/MaskManager.js @@ -0,0 +1,105 @@ +var WebGLManager = require('./WebGLManager'), + AlphaMaskFilter = require('../utils/SpriteMaskFilter'), + utils = require('../../../utils'); + +/** + * @class + * @namespace PIXI + * @param renderer {WebGLRenderer} The renderer this manager works for. + */ +function MaskManager(renderer) +{ + WebGLManager.call(this, renderer); + + this.stencilStack = []; + this.reverse = true; + this.count = 0; + + this.alphaMaskPool = []; +} + +MaskManager.prototype = Object.create(WebGLManager.prototype); +MaskManager.prototype.constructor = MaskManager; +module.exports = MaskManager; + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param graphics {Graphics} + * @param webGLData {any[]} + */ + +MaskManager.prototype.destroy = function () +{ + +}; + +MaskManager.prototype.pushMask = function (target, maskData) +{ + if(maskData.texture) + { + this.pushSpriteMask(target, maskData); + } + else + { + this.pushStencilMask(target, maskData); + } + +} + +MaskManager.prototype.popMask = function (target, maskData) +{ + if(maskData.texture) + { + this.popSpriteMask(target, maskData); + } + else + { + this.popStencilMask(target, maskData); + } +} + +MaskManager.prototype.pushSpriteMask = function (target, maskData) +{ + var alphaMaskFilter = this.alphaMaskPool.pop(); + + if(!alphaMaskFilter)alphaMaskFilter = [ new AlphaMaskFilter( maskData ) ]; + + this.renderer.filterManager.pushFilter(target, alphaMaskFilter) +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popSpriteMask = function (maskData) +{ + + var filters = this.renderer.filterManager.popFilter(); + + this.alphaMaskPool.push(filters); +}; + + +/** + * Applies the Mask and adds it to the current filter stack. + * + * @param maskData {any[]} + */ + +MaskManager.prototype.pushStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.pushMask(maskData); +} + +/** + * Removes the last filter from the filter stack and doesn't return it. + * + * @param maskData {any[]} + */ +MaskManager.prototype.popStencilMask = function (target, maskData) +{ + this.renderer.stencilManager.popMask(maskData); +}; + diff --git a/src/core/renderers/webgl/managers/StencilManager.js b/src/core/renderers/webgl/managers/StencilManager.js new file mode 100644 index 0000000..aecfcac --- /dev/null +++ b/src/core/renderers/webgl/managers/StencilManager.js @@ -0,0 +1,318 @@ +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.plugins.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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + + 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.plugins.graphics); + + if (maskData.dirty) + { + this.renderer.plugins.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.plugins.graphics); + + this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); +}; + diff --git a/src/core/renderers/webgl/managers/WebGLFilterManager.js b/src/core/renderers/webgl/managers/WebGLFilterManager.js index b91b80a..a522031 100644 --- a/src/core/renderers/webgl/managers/WebGLFilterManager.js +++ b/src/core/renderers/webgl/managers/WebGLFilterManager.js @@ -1,7 +1,8 @@ var WebGLManager = require('./WebGLManager'), FilterTexture = require('../utils/FilterTexture'), RenderTarget = require('../utils/RenderTarget'); - Shader = require('../shaders/Shader'), + DefaultShader = require('../shaders/DefaultShader'), + Quad = require('./Quad'), math = require('../../../math'); @@ -10,10 +11,12 @@ * @namespace PIXI * @param renderer {WebGLRenderer} The renderer this manager works for. */ -function WebGLFilterManager(renderer) +function FilterManager(renderer) { WebGLManager.call(this, renderer); + this.count = 0; + /** * @member {any[]} */ @@ -31,30 +34,31 @@ this.texturePool = []; // listen for context and update necessary buffers - var self = this; - this.renderer.on('context', function () - { - self.texturePool.length = 0; - self.initShaderBuffers(); - }); + //TODO make this dynamic! + this.textureSize = new math.Rectangle(0, 0, 800, 600); - this.textureSize = new math.Rectangle(0,0,800, 600); - - + this.tempMatrix = new math.Matrix(); } -WebGLFilterManager.prototype = Object.create(WebGLManager.prototype); -WebGLFilterManager.prototype.constructor = WebGLFilterManager; -module.exports = WebGLFilterManager; +FilterManager.prototype = Object.create(WebGLManager.prototype); +FilterManager.prototype.constructor = FilterManager; +module.exports = FilterManager; + + +FilterManager.prototype.onContextChange = function () +{ + this.texturePool.length = 0; + this.initShaderBuffers(); +} /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -WebGLFilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function (buffer) { -// this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; - //console.log("<-------->") + //TODO sort out bounds - no point creating a new rect each frame! + //this.defaultShader = this.renderer.shaderManager.plugins.defaultShader; this.filterStack[0].renderTarget = this.renderer.currentRenderTarget; this.filterStack[0].bounds = new math.Rectangle(0, 0, this.renderer.currentRenderTarget.width, this.renderer.currentRenderTarget.height); }; @@ -64,14 +68,14 @@ * * @param filterBlock {object} the filter that will be pushed to the current filter stack */ -WebGLFilterManager.prototype.pushFilter = function (target, filters) +FilterManager.prototype.pushFilter = function (target, filters) { var gl = this.renderer.gl; //console.log("push") // get the bounds of the object.. var bounds = target.filterArea || target.getBounds(); - this.realSize = bounds;//.clone(); + this.capFilterArea( bounds ); var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); @@ -79,14 +83,13 @@ // TODO setting frame is lame.. texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); texture.activate(); + + // clear the texture.. texture.clear(); - texture.frame = null; this.renderer.currentRenderTarget = texture; - - - // TODO get rid of object creation! + // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, filter:filters, @@ -100,7 +103,7 @@ * Removes the last filter from the filter stack and doesn't return it. * */ -WebGLFilterManager.prototype.popFilter = function () +FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); @@ -112,16 +115,14 @@ var filter = filterData.filter[0]; var shader = filter.shaders[gl.id]; - // shader.syncUniforms(); - + // shader.syncUniforms(); this.quad.map(this.textureSize, filterData.bounds); // - // if (!shader) { - shader = new Shader(this, + shader = new DefaultShader(this, filter.vertexSrc, filter.fragmentSrc, filter.uniforms, @@ -152,67 +153,120 @@ this.renderer.currentRenderTarget.frame = previousFilter.bounds; this.renderer.currentRenderTarget.activate(); - this.renderer.currentRenderTarget.frame = null; - + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + var m = this.calculateMappedMatrix(filterData.bounds, filter.sprite) + /// m.ty = 0.1; + //m.translate(0.5,0.5) + // m.a = 2; + gl.uniformMatrix3fv(shader.uniforms.otherMatrix._location, false, m.toArray(true)); + + /// custom // + this.textureCount = 1; + gl.activeTexture(gl.TEXTURE1); + + var maskTexture = filter.uniforms.mask.value.baseTexture; + + if (!maskTexture._glTextures[gl.id]) + { + this.renderer.updateTexture(maskTexture); + } + else + { + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + } + + + // set uniform to texture index + gl.uniform1i(filter.uniforms.mask._location, 1); + + // increment next texture id + this.textureCount++; + + + gl.activeTexture(gl.TEXTURE0); gl.bindTexture(gl.TEXTURE_2D, input.texture); gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); this.texturePool.push(filterData.renderTarget); + + return filterData.filter; }; -// this.projectionMatrix.d = 1/height*2; - // offset.. +// TODO playing around here.. this is temporary - (will end up in the shader) +FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) +{ + var worldTransform = sprite.worldTransform.copy(math.Matrix.TEMP_MATRIX); + var texture = sprite.texture.baseTexture; + var mappedMatrix = this.tempMatrix.identity(); + + // scale.. + var ratio = this.textureSize.height / this.textureSize.width; + + mappedMatrix.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height ); - + mappedMatrix.scale(1 , ratio); - // ----- // + var translateScaleX = (this.textureSize.width / texture.width); + var translateScaleY = (this.textureSize.height / texture.height); + + worldTransform.tx /= texture.width * translateScaleX; + worldTransform.ty /= texture.width * translateScaleX; + + worldTransform.invert(); + + mappedMatrix.prepend(worldTransform); + + // apply inverse scale.. + mappedMatrix.scale(1 , 1/ratio); + + mappedMatrix.scale( translateScaleX , translateScaleY ); + + mappedMatrix.translate(sprite.anchor.x, sprite.anchor.y); + + return mappedMatrix; + + // Keeping the orginal as a reminder to me on how this works! + // + // var m = new math.Matrix(); + + // // scale.. + // var ratio = this.textureSize.height / this.textureSize.width; + + // m.translate(filterArea.x / this.textureSize.width, filterArea.y / this.textureSize.height); - // this.texture. - /* - var projection = this.renderer.projection; - var offset = this.renderer.offset; + // m.scale(1 , ratio); - filterBlock._filterArea = filterBlock.target.filterArea || filterBlock.target.getBounds(); - // filter program - // OPTIMISATION - the first filter is free if its a simple color change? - this.filterStack.push(filterBlock); + // var transform = wt.clone(); + + // var translateScaleX = (this.textureSize.width / 620); + // var translateScaleY = (this.textureSize.height / 380); - var filter = filterBlock.filterPasses[0]; + // transform.tx /= 620 * translateScaleX; + // transform.ty /= 620 * translateScaleX; - this.offsetX += filterBlock._filterArea.x; - this.offsetY += filterBlock._filterArea.y; + // transform.invert(); - var texture = this.texturePool.pop(); - if (!texture) - { - texture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - else - { - texture.resize(this.width, this.height); - } + // transform.append(m); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); + // // apply inverse scale.. + // transform.scale(1 , 1/ratio); - var filterArea = filterBlock._filterArea;// filterBlock.target.getBounds();///filterBlock.target.filterArea; + // transform.scale( translateScaleX , translateScaleY ); - var padding = filter.padding; - filterArea.x -= padding; - filterArea.y -= padding; - filterArea.width += padding * 2; - filterArea.height += padding * 2; + // return transform; +} - var localX = filterArea.x, - localY = filterArea.y; - +FilterManager.prototype.capFilterArea = function (filterArea) +{ if (filterArea.x < 0) { filterArea.width += filterArea.x; @@ -225,347 +279,37 @@ filterArea.y = 0; } - if (localX + filterArea.width > this.width) + if ( filterArea.x + filterArea.width > this.textureSize.width ) { - filterArea.width = this.width - localX; + filterArea.width = this.textureSize.width - filterArea.x; } - if (localY + filterArea.height > this.height) + if ( filterArea.y + filterArea.height > this.textureSize.height ) { - filterArea.height = this.height - localY; + filterArea.height = this.textureSize.height - filterArea.y; } - - if (filterArea.width < 0) - { - filterArea.width = 0; - } - - if (filterArea.height < 0) - { - filterArea.height = 0; - } - - //gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, filterArea.width, filterArea.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); - gl.bindFramebuffer(gl.FRAMEBUFFER, texture.frameBuffer); - - // set view port - gl.viewport(0, 0, filterArea.width, filterArea.height); - - projection.x = filterArea.width/2; - projection.y = -filterArea.height/2; - - offset.x = -filterArea.x; - offset.y = -filterArea.y; - - // update projection - // now restore the regular shader.. - // this.renderer.shaderManager.setShader(this.defaultShader); - //gl.uniform2f(this.defaultShader.projectionVector, filterArea.width/2, -filterArea.height/2); - //gl.uniform2f(this.defaultShader.offsetVector, -filterArea.x, -filterArea.y); - - gl.colorMask(true, true, true, true); - gl.clearColor(0,0,0, 0); - gl.clear(gl.COLOR_BUFFER_BIT); - - filterBlock._glFilterTexture = texture; -` */ - - // this.texturePool.push(output); - - /* - var gl = this.renderer.gl; - - var filterBlock = this.filterStack.pop(); - var filterArea = filterBlock._filterArea; - var texture = filterBlock._glFilterTexture; - var projection = this.renderer.projection; - var offset = this.renderer.offset; - - if (filterBlock.filterPasses.length > 1) - { - gl.viewport(0, 0, filterArea.width, filterArea.height); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = 0; - this.vertexArray[1] = filterArea.height; - - this.vertexArray[2] = filterArea.width; - this.vertexArray[3] = filterArea.height; - - this.vertexArray[4] = 0; - this.vertexArray[5] = 0; - - this.vertexArray[6] = filterArea.width; - this.vertexArray[7] = 0; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - // now set the uvs.. - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - var inputTexture = texture; - var outputTexture = this.texturePool.pop(); - if (!outputTexture) - { - outputTexture = new FilterTexture(this.renderer.gl, this.width, this.height); - } - outputTexture.resize(this.width, this.height); - - // need to clear this FBO as it may have some left over elements from a previous filter. - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - gl.clear(gl.COLOR_BUFFER_BIT); - - gl.disable(gl.BLEND); - - for (var i = 0; i < filterBlock.filterPasses.length-1; i++) - { - var filterPass = filterBlock.filterPasses[i]; - - gl.bindFramebuffer(gl.FRAMEBUFFER, outputTexture.frameBuffer ); - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, inputTexture.texture); - - // draw texture.. - //filterPass.applyFilterPass(filterArea.width, filterArea.height); - this.applyFilterPass(filterPass, filterArea, filterArea.width, filterArea.height); - - // swap the textures.. - var temp = inputTexture; - inputTexture = outputTexture; - outputTexture = temp; - } - - gl.enable(gl.BLEND); - - texture = inputTexture; - this.texturePool.push(outputTexture); - } - - var filter = filterBlock.filterPasses[filterBlock.filterPasses.length-1]; - - this.offsetX -= filterArea.x; - this.offsetY -= filterArea.y; - - var sizeX = this.width; - var sizeY = this.height; - - var offsetX = 0; - var offsetY = 0; - - var buffer = this.buffer; - - // time to render the filters texture to the previous scene - if (this.filterStack.length === 0) - { - gl.colorMask(true, true, true, true);//this.transparent); - } - else - { - var currentFilter = this.filterStack[this.filterStack.length-1]; - filterArea = currentFilter._filterArea; - - sizeX = filterArea.width; - sizeY = filterArea.height; - - offsetX = filterArea.x; - offsetY = filterArea.y; - - buffer = currentFilter._glFilterTexture.frameBuffer; - } - - // TODO need to remove these global elements.. - projection.x = sizeX/2; - projection.y = -sizeY/2; - - offset.x = offsetX; - offset.y = offsetY; - - filterArea = filterBlock._filterArea; - - var x = filterArea.x-offsetX; - var y = filterArea.y-offsetY; - - // update the buffers.. - // make sure to flip the y! - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - - this.vertexArray[0] = x; - this.vertexArray[1] = y + filterArea.height; - - this.vertexArray[2] = x + filterArea.width; - this.vertexArray[3] = y + filterArea.height; - - this.vertexArray[4] = x; - this.vertexArray[5] = y; - - this.vertexArray[6] = x + filterArea.width; - this.vertexArray[7] = y; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.vertexArray); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - - this.uvArray[2] = filterArea.width/this.width; - this.uvArray[5] = filterArea.height/this.height; - this.uvArray[6] = filterArea.width/this.width; - this.uvArray[7] = filterArea.height/this.height; - - gl.bufferSubData(gl.ARRAY_BUFFER, 0, this.uvArray); - - gl.viewport(0, 0, sizeX, sizeY); - - // bind the buffer - gl.bindFramebuffer(gl.FRAMEBUFFER, buffer ); - - // set the blend mode! - //gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA) - - // set texture - gl.activeTexture(gl.TEXTURE0); - gl.bindTexture(gl.TEXTURE_2D, texture.texture); - - // apply! - this.applyFilterPass(filter, filterArea, sizeX, sizeY); - - // now restore the regular shader.. should happen automatically now.. - // this.renderer.shaderManager.setShader(this.defaultShader); - // gl.uniform2f(this.defaultShader.projectionVector, sizeX/2, -sizeY/2); - // gl.uniform2f(this.defaultShader.offsetVector, -offsetX, -offsetY); - - // return the texture to the pool - filterBlock._glFilterTexture = null; - */ - -/** - * Applies the filter to the specified area. - * - * @param filter {AbstractFilter} the filter that needs to be applied - * @param filterArea {Texture} TODO - might need an update - * @param width {number} the horizontal range of the filter - * @param height {number} the vertical range of the filter - */ -WebGLFilterManager.prototype.applyFilterPass = function (filter, filterArea, width, height) -{ - // use program - var gl = this.renderer.gl; - - var shader = filter.shaders[gl.id]; - - if (!shader) - { - shader = new Shader(this.renderer.shaderManager); - - shader.fragmentSrc = filter.fragmentSrc; - shader.uniforms = filter.uniforms; - shader.init(); - - filter.shaders[gl.id] = shader; - } - - // set the shader - this.renderer.shaderManager.setShader(shader); - -// gl.useProgram(shader.program); - - gl.uniform2f(shader.projectionVector, width/2, -height/2); - gl.uniform2f(shader.offsetVector, 0,0); - - if (filter.uniforms.dimensions) - { - filter.uniforms.dimensions.value[0] = this.width;//width; - filter.uniforms.dimensions.value[1] = this.height;//height; - filter.uniforms.dimensions.value[2] = this.vertexArray[0]; - filter.uniforms.dimensions.value[3] = this.vertexArray[5];//filterArea.height; - } - - shader.syncUniforms(); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.vertexAttribPointer(shader.aVertexPosition, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.vertexAttribPointer(shader.aTextureCoord, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.vertexAttribPointer(shader.aColor, 2, gl.FLOAT, false, 0, 0); - - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - - // draw the filter... - gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); +} - this.renderer.drawCount++; -}; /** * Initialises the shader buffers. * */ -WebGLFilterManager.prototype.initShaderBuffers = function () +FilterManager.prototype.initShaderBuffers = function () { var gl = this.renderer.gl; - this.quad = new Quad(gl); - - // create some buffers - this.vertexBuffer = gl.createBuffer(); - this.uvBuffer = gl.createBuffer(); - this.colorBuffer = gl.createBuffer(); - this.indexBuffer = gl.createBuffer(); - - // bind and upload the vertexs.. - // keep a reference to the vertexFloatData.. - this.vertexArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.vertexArray, gl.STATIC_DRAW); - - // bind and upload the uv buffer - this.uvArray = new Float32Array([0.0, 0.0, - 1.0, 0.0, - 0.0, 1.0, - 1.0, 1.0]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.uvBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.uvArray, gl.STATIC_DRAW); - - this.colorArray = new Float32Array([1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF, - 1.0, 0xFFFFFF]); - - gl.bindBuffer(gl.ARRAY_BUFFER, this.colorBuffer); - gl.bufferData(gl.ARRAY_BUFFER, this.colorArray, gl.STATIC_DRAW); - - // bind and upload the index - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); - gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array([0, 1, 2, 1, 3, 2]), gl.STATIC_DRAW); - }; /** * Destroys the filter and removes it from the filter stack. * */ -WebGLFilterManager.prototype.destroy = function () +FilterManager.prototype.destroy = function () { var gl = this.renderer.gl; this.filterStack = null; - - this.offsetX = 0; this.offsetY = 0; // destroy textures @@ -575,12 +319,4 @@ } this.texturePool = null; - - //destroy buffers.. - gl.deleteBuffer(this.vertexBuffer); - gl.deleteBuffer(this.uvBuffer); - gl.deleteBuffer(this.colorBuffer); - gl.deleteBuffer(this.indexBuffer); - - this.renderer = null; }; diff --git a/src/core/renderers/webgl/managers/WebGLManager.js b/src/core/renderers/webgl/managers/WebGLManager.js index c30cd51..198f28e 100644 --- a/src/core/renderers/webgl/managers/WebGLManager.js +++ b/src/core/renderers/webgl/managers/WebGLManager.js @@ -11,11 +11,23 @@ * @member {WebGLRenderer} */ this.renderer = renderer; + + var self = this; + this.renderer.on('context', function(){ + + self.onContextChange(); + + }); } WebGLManager.prototype.constructor = WebGLManager; module.exports = WebGLManager; +WebGLManager.prototype.onContextChange = function () +{ + // do some codes init! +} + WebGLManager.prototype.destroy = function () { this.renderer = null; diff --git a/src/core/renderers/webgl/managers/WebGLMaskManager.js b/src/core/renderers/webgl/managers/WebGLMaskManager.js deleted file mode 100644 index d6a11c1..0000000 --- a/src/core/renderers/webgl/managers/WebGLMaskManager.js +++ /dev/null @@ -1,321 +0,0 @@ -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.plugins.primitiveShader; - - if (webGLData.mode === 1) - { - shader = this.renderer.shaderManager.plugins.complexPrimitiveShader; - - this.renderer.shaderManager.setShader(shader); - - gl.uniformMatrix3fv(shader.uniforms.translationMatrix._location, false, graphics.worldTransform.toArray(true)); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); - - 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.plugins.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.plugins.graphics); - - if (maskData.dirty) - { - this.renderer.plugins.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.plugins.graphics); - - this.popStencil(maskData, maskData._webGL[this.renderer.gl.id].data[0], this.renderer); -}; - diff --git a/src/core/renderers/webgl/managers/WebGLShaderManager.js b/src/core/renderers/webgl/managers/WebGLShaderManager.js index fecafd1..08585b7 100644 --- a/src/core/renderers/webgl/managers/WebGLShaderManager.js +++ b/src/core/renderers/webgl/managers/WebGLShaderManager.js @@ -1,4 +1,7 @@ var WebGLManager = require('./WebGLManager'), + DefaultShader = require('../shaders/DefaultShader'), + ComplexPrimitiveShader = require('../shaders/ComplexPrimitiveShader'), + PrimitiveShader = require('../shaders/PrimitiveShader'), utils = require('../../../utils'); /** @@ -51,15 +54,7 @@ // listen for context and update necessary shaders var self = this; - this.renderer.on('context', function () - { - for (var o in this.plugins) - { - this.plugins[o] = new (this.plugins[o].constructor)(self); - } - self.setShader(self.defaultShader); - }); } WebGLShaderManager.prototype = Object.create(WebGLManager.prototype); @@ -68,6 +63,18 @@ module.exports = WebGLShaderManager; +WebGLShaderManager.prototype.onContextChange = function () +{ + for (var o in this.plugins) + { + this.plugins[o] = new (this.plugins[o].constructor)(self); + } + + this.defaultShader = new DefaultShader(this); + // init webGL stuff! + this.primitiveShader = new PrimitiveShader(this); + this.complexPrimitiveShader = new ComplexPrimitiveShader(this); +} /** diff --git a/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js new file mode 100644 index 0000000..6bb8b2e --- /dev/null +++ b/src/core/renderers/webgl/shaders/ComplexPrimitiveShader.js @@ -0,0 +1,57 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function ComplexPrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform vec3 tint;', + 'uniform float alpha;', + 'uniform vec3 color;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = vec4(color * alpha * tint, alpha);',//" * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + color: { type: '3f', value: [0,0,0] }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // attributes + { + aVertexPosition:0 + } + ); +} + +ComplexPrimitiveShader.prototype = Object.create(Shader.prototype); +ComplexPrimitiveShader.prototype.constructor = ComplexPrimitiveShader; +module.exports = ComplexPrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/DefaultShader.js b/src/core/renderers/webgl/shaders/DefaultShader.js new file mode 100644 index 0000000..77fe02a --- /dev/null +++ b/src/core/renderers/webgl/shaders/DefaultShader.js @@ -0,0 +1,91 @@ +var utils = require('../../../utils'), + Shader = require('./Shader'); +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + * @param [vertexSrc] {string} The source of the vertex shader. + * @param [fragmentSrc] {string} The source of the fragment shader. + * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. + * @param [fragmentSrc] {string} The source of the fragment shader. + */ +function DefaultShader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +{ + var uniforms = { + + uSampler: { type: 'sampler2D', value: 0 }, + projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) }, + }; + + if(customUniforms) + { + for (var u in customUniforms) + { + uniforms[u] = customUniforms[u]; + } + } + + + var attributes = { + aVertexPosition: 0, + aTextureCoord: 0, + aColor: 0 + }; + + if(customAttributes) + { + for (var a in attributes) + { + attributes[a] = customAttributes[a]; + } + } + + /** + * The vertex shader. + * @member {Array} + */ + vertexSrc = vertexSrc || [ + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + '}' + ].join('\n'); + + /** + * The fragment shader. + * @member {Array} + */ + fragmentSrc = fragmentSrc || [ + 'precision lowp float;', + + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + + 'void main(void){', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', + '}' + ].join('\n'); + + Shader.call(this, shaderManager, vertexSrc, fragmentSrc, uniforms, attributes); +} + +// constructor +DefaultShader.prototype = Object.create(Shader.prototype); +DefaultShader.prototype.constructor = DefaultShader; +module.exports = DefaultShader; diff --git a/src/core/renderers/webgl/shaders/PrimitiveShader.js b/src/core/renderers/webgl/shaders/PrimitiveShader.js new file mode 100644 index 0000000..cad38fd --- /dev/null +++ b/src/core/renderers/webgl/shaders/PrimitiveShader.js @@ -0,0 +1,58 @@ +var Shader = require('./Shader'); + +/** + * @class + * @namespace PIXI + * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. + */ +function PrimitiveShader(shaderManager) +{ + Shader.call(this, + shaderManager, + // vertex shader + [ + 'attribute vec2 aVertexPosition;', + 'attribute vec4 aColor;', + + 'uniform mat3 translationMatrix;', + 'uniform mat3 projectionMatrix;', + + 'uniform float alpha;', + 'uniform float flipY;', + 'uniform vec3 tint;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_Position = vec4((projectionMatrix * translationMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vColor = aColor * vec4(tint * alpha, alpha);', + '}' + ].join('\n'), + // fragment shader + [ + 'precision mediump float;', + + 'varying vec4 vColor;', + + 'void main(void){', + ' gl_FragColor = vColor;', + '}' + ].join('\n'), + // custom uniforms + { + tint: { type: '3f', value: [0, 0, 0] }, + alpha: { type: '1f', value: 0 }, + translationMatrix: { type: 'mat3', value: new Float32Array(9) }, + projectionMatrix: { type: 'mat3', value: new Float32Array(9) } + }, + // custom attributes + { + aVertexPosition:0, + aColor:0 + } + ); +} + +PrimitiveShader.prototype = Object.create(Shader.prototype); +PrimitiveShader.prototype.constructor = PrimitiveShader; +module.exports = PrimitiveShader; diff --git a/src/core/renderers/webgl/shaders/Shader.js b/src/core/renderers/webgl/shaders/Shader.js index 4452369..35b98d6 100644 --- a/src/core/renderers/webgl/shaders/Shader.js +++ b/src/core/renderers/webgl/shaders/Shader.js @@ -1,5 +1,4 @@ -var utils = require('../../../utils'), - WebGLShaderManager = require('../managers/WebGLShaderManager'); +var utils = require('../../../utils'); /** * @class @@ -10,8 +9,7 @@ * @param customUniforms {object} Custom uniforms to use to augment the built-in ones. * @param [fragmentSrc] {string} The source of the fragment shader. */ -//TODO change shaderManager to gl -function Shader(shaderManager, vertexSrc, fragmentSrc, customUniforms, customAttributes) +function Shader(shaderManager, vertexSrc, fragmentSrc, uniforms, attributes) { /** * @member {number} @@ -32,31 +30,9 @@ */ this.program = null; - this.uniforms = { - uSampler: { type: 'sampler2D', value: 0 }, - projectionVector: { type: '2f', value: { x: 0, y: 0 } }, - offsetVector: { type: '2f', value: { x: 0, y: 0 } }, - projectionMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, - 0, 1, 0, - 0, 0, 1) }, - dimensions: { type: '4f', value: new Float32Array(4) } - }; + this.uniforms = uniforms || {} - for (var u in customUniforms) - { - this.uniforms[u] = customUniforms[u]; - } - - this.attributes = { - aVertexPosition: 0, - aTextureCoord: 0, - aColor: 0 - }; - - for (var a in customAttributes) - { - this.attributes[a] = customAttributes[a]; - } + this.attributes = attributes || {}; this.textureCount = 0; @@ -70,8 +46,6 @@ 'attribute vec4 aColor;', 'uniform mat3 projectionMatrix;', - // 'uniform vec2 projectionVector;', - 'uniform vec2 offsetVector;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -79,9 +53,7 @@ 'const vec2 center = vec2(-1.0, 1.0);', 'void main(void){', - // ' gl_Position = vec4( ((aVertexPosition + offsetVector) / projectionVector) + center , 0.0, 1.0);', - // ' gl_Position = vec4( projectionMatrix * vec3(aVertexPosition, 1.0) ), 0.0, 1.0);', - 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', ' vTextureCoord = aTextureCoord;', ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', '}' @@ -110,8 +82,6 @@ Shader.prototype.constructor = Shader; module.exports = Shader; -WebGLShaderManager.registerPlugin('defaultShader', Shader); - Shader.prototype.init = function () { this.compile(); @@ -120,11 +90,6 @@ this.cacheUniformLocations(Object.keys(this.uniforms)); this.cacheAttributeLocations(Object.keys(this.attributes)); - - - //this.buildSync(); - - // console.log(this.syncUniforms) }; Shader.prototype.cacheUniformLocations = function (keys) @@ -194,262 +159,281 @@ return (this.program = program); }; +/* Shader.prototype.buildSync = function () { - var str = "" + // var str = "" - str = "Shader.prototype.syncUniforms = function()"; - str += "{\n"; + // str = "Shader.prototype.syncUniforms = function()"; + // str += "{\n"; for (var key in this.uniforms) { var uniform = this.uniforms[key]; + + Object.defineProperty(this, key, { + + get: function () + { + return uniform.value + }, + set: function (value) + { + this.setUniform(uniform, value); + } + }); + + console.log( makePropSetter( key, " bloop", uniform.type ) ) + // Object.def // location = uniform._location, // value = uniform.value, //i, il; - str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" + // str += "gl.uniform1i(this.uniforms."+ key +"._location, this.uniforms." + key + ".value );\n" } - str += "}"; - // console.log(str) - eval(str); +}*/ + +Shader.prototype.syncUniform = function (uniform) +{ + var location = uniform._location, + value = uniform.value, + gl = this.gl, + i, il; + + switch (uniform.type) + { + // single int value + case 'i': + case '1i': + gl.uniform1i(location, value); + break; + + // single float value + case 'f': + case '1f': + gl.uniform1f(location, value); + break; + + // Float32Array(2) or JS Arrray + case '2f': + gl.uniform2f(location, value[0], value[1]); + break; + + // Float32Array(3) or JS Arrray + case '3f': + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // Float32Array(4) or JS Arrray + case '4f': + gl.uniform4f(location, value[0], value[1], value[2], value[3]); + break; + + // a 2D Point object + case 'v2': + gl.uniform2f(location, value.x, value.y); + break; + + // a 3D Point object + case 'v3': + gl.uniform3f(location, value.x, value.y, value.z); + break; + + // a 4D Point object + case 'v4': + gl.uniform4f(location, value.x, value.y, value.z, value.w); + break; + + // Int32Array or JS Array + case '1iv': + gl.uniform1iv(location, value); + break; + + // Int32Array or JS Array + case '2iv': + gl.uniform2iv(location, value); + break; + + // Int32Array or JS Array + case '3iv': + gl.uniform3iv(location, value); + break; + + // Int32Array or JS Array + case '4iv': + gl.uniform4iv(location, value); + break; + + // Float32Array or JS Array + case '1fv': + gl.uniform1fv(location, value); + break; + + // Float32Array or JS Array + case '2fv': + gl.uniform2fv(location, value); + break; + + // Float32Array or JS Array + case '3fv': + gl.uniform3fv(location, value); + break; + + // Float32Array or JS Array + case '4fv': + gl.uniform4fv(location, value); + break; + + // Float32Array or JS Array + case 'm2': + case 'mat2': + case 'Matrix2fv': + gl.uniformMatrix2fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm3': + case 'mat3': + case 'Matrix3fv': + + gl.uniformMatrix3fv(location, uniform.transpose, value); + break; + + // Float32Array or JS Array + case 'm4': + case 'mat4': + case 'Matrix4fv': + gl.uniformMatrix4fv(location, uniform.transpose, value); + break; + + // a Color Value + case 'c': + if (typeof value === 'number') + { + value = utils.hex2rgb(value); + } + + gl.uniform3f(location, value[0], value[1], value[2]); + break; + + // flat array of integers (JS or typed array) + case 'iv1': + gl.uniform1iv(location, value); + break; + + // flat array of integers with 3 x N size (JS or typed array) + case 'iv': + gl.uniform3iv(location, value); + break; + + // flat array of floats (JS or typed array) + case 'fv1': + gl.uniform1fv(location, value); + break; + + // flat array of floats with 3 x N size (JS or typed array) + case 'fv': + gl.uniform3fv(location, value); + break; + + // array of 2D Point objects + case 'v2v': + if (!uniform._array) + { + uniform._array = new Float32Array(2 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 2] = value[i].x; + uniform._array[i * 2 + 1] = value[i].y; + } + + gl.uniform2fv(location, uniform._array); + break; + + // array of 3D Point objects + case 'v3v': + if (!uniform._array) + { + uniform._array = new Float32Array(3 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 3] = value[i].x; + uniform._array[i * 3 + 1] = value[i].y; + uniform._array[i * 3 + 2] = value[i].z; + + } + + gl.uniform3fv(location, uniform._array); + break; + + // array of 4D Point objects + case 'v4v': + if (!uniform._array) + { + uniform._array = new Float32Array(4 * value.length); + } + + for (i = 0, il = value.length; i < il; ++i) + { + uniform._array[i * 4] = value[i].x; + uniform._array[i * 4 + 1] = value[i].y; + uniform._array[i * 4 + 2] = value[i].z; + uniform._array[i * 4 + 3] = value[i].w; + + } + + gl.uniform4fv(location, uniform._array); + break; + + // PIXI.Texture + case 't': + case 'sampler2D': + if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) + { + break; + } + + // activate this texture + gl.activeTexture(gl['TEXTURE' + this.textureCount]); + + // bind the texture + gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); + + // set uniform to texture index + gl.uniform1i(uniform._location, this.textureCount); + + // increment next texture id + this.textureCount++; + + // initialize the texture if we haven't yet + if (!uniform._init) + { + this.initSampler2D(uniform); + + uniform._init = true; + } + break; + + default: + window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); + } } Shader.prototype.syncUniforms = function () { var gl = this.gl; - this.textureCount = 1; + this.textureCount = 0; for (var key in this.uniforms) { - var uniform = this.uniforms[key], - location = uniform._location, - value = uniform.value, - i, il; - - switch (uniform.type) - { - // single int value - case 'i': - case '1i': - gl.uniform1i(location, value); - break; - - // single float value - case 'f': - case '1f': - gl.uniform1f(location, value); - break; - - // Float32Array(2) or JS Arrray - case '2f': - gl.uniform2f(location, value[0], value[1]); - break; - - // Float32Array(3) or JS Arrray - case '3f': - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // Float32Array(4) or JS Arrray - case '4f': - gl.uniform4f(location, value[0], value[1], value[2], value[3]); - break; - - // a 2D Point object - case 'v2': - gl.uniform2f(location, value.x, value.y); - break; - - // a 3D Point object - case 'v3': - gl.uniform3f(location, value.x, value.y, value.z); - break; - - // a 4D Point object - case 'v4': - gl.uniform4f(location, value.x, value.y, value.z, value.w); - break; - - // Int32Array or JS Array - case '1iv': - gl.uniform1iv(location, value); - break; - - // Int32Array or JS Array - case '2iv': - gl.uniform2iv(location, value); - break; - - // Int32Array or JS Array - case '3iv': - gl.uniform3iv(location, value); - break; - - // Int32Array or JS Array - case '4iv': - gl.uniform4iv(location, value); - break; - - // Float32Array or JS Array - case '1fv': - gl.uniform1fv(location, value); - break; - - // Float32Array or JS Array - case '2fv': - gl.uniform2fv(location, value); - break; - - // Float32Array or JS Array - case '3fv': - gl.uniform3fv(location, value); - break; - - // Float32Array or JS Array - case '4fv': - gl.uniform4fv(location, value); - break; - - // Float32Array or JS Array - case 'm2': - case 'mat2': - case 'Matrix2fv': - gl.uniformMatrix2fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm3': - case 'mat3': - case 'Matrix3fv': - gl.uniformMatrix3fv(location, uniform.transpose, value); - break; - - // Float32Array or JS Array - case 'm4': - case 'mat4': - case 'Matrix4fv': - gl.uniformMatrix4fv(location, uniform.transpose, value); - break; - - // a Color Value - case 'c': - if (typeof value === 'number') - { - value = utils.hex2rgb(value); - } - - gl.uniform3f(location, value[0], value[1], value[2]); - break; - - // flat array of integers (JS or typed array) - case 'iv1': - gl.uniform1iv(location, value); - break; - - // flat array of integers with 3 x N size (JS or typed array) - case 'iv': - gl.uniform3iv(location, value); - break; - - // flat array of floats (JS or typed array) - case 'fv1': - gl.uniform1fv(location, value); - break; - - // flat array of floats with 3 x N size (JS or typed array) - case 'fv': - gl.uniform3fv(location, value); - break; - - // array of 2D Point objects - case 'v2v': - if (!uniform._array) - { - uniform._array = new Float32Array(2 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 2] = value[i].x; - uniform._array[i * 2 + 1] = value[i].y; - } - - gl.uniform2fv(location, uniform._array); - break; - - // array of 3D Point objects - case 'v3v': - if (!uniform._array) - { - uniform._array = new Float32Array(3 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 3] = value[i].x; - uniform._array[i * 3 + 1] = value[i].y; - uniform._array[i * 3 + 2] = value[i].z; - - } - - gl.uniform3fv(location, uniform._array); - break; - - // array of 4D Point objects - case 'v4v': - if (!uniform._array) - { - uniform._array = new Float32Array(4 * value.length); - } - - for (i = 0, il = value.length; i < il; ++i) - { - uniform._array[i * 4] = value[i].x; - uniform._array[i * 4 + 1] = value[i].y; - uniform._array[i * 4 + 2] = value[i].z; - uniform._array[i * 4 + 3] = value[i].w; - - } - - gl.uniform4fv(location, uniform._array); - break; - - // PIXI.Texture - case 't': - case 'sampler2D': - if (!uniform.value || !uniform.value.baseTexture || !uniform.value.baseTexture.hasLoaded) - { - break; - } - - // activate this texture - gl.activeTexture(gl['TEXTURE' + this.textureCount]); - - // bind the texture - gl.bindTexture(gl.TEXTURE_2D, uniform.value.baseTexture._glTextures[gl.id]); - - // set uniform to texture index - gl.uniform1i(uniform._location, this.textureCount); - - // increment next texture id - this.textureCount++; - - // initialize the texture if we haven't yet - if (!uniform._init) - { - this.initSampler2D(uniform); - - uniform._init = true; - } - break; - - default: - window.console.warn('Pixi.js Shader Warning: Unknown uniform type: ' + uniform.type); - } + this.syncUniform(); } }; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js new file mode 100644 index 0000000..e9340a0 --- /dev/null +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -0,0 +1,65 @@ +/** + * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. + * If you want to make a custom filter this should be your base class. + * + * @class + * @namespace PIXI + * @param fragmentSrc {string|string[]} The fragment source in an array of strings. + * @param uniforms {object} An object containing the uniforms for this filter. + */ +function AbstractFilter(fragmentSrc, uniforms) +{ + /** + * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. + * For example the blur filter has two passes blurX and blurY. + * + * @member {AbstractFilter[]} + * @private + */ + this.passes = [this]; + + /** + * @member {Shader[]} + * @private + */ + this.shaders = []; + + /** + * @member {number} + */ + this.padding = 0; + + /** + * @member {object} + * @private + */ + this.uniforms = uniforms || {}; + + /** + * @member {string[]} + * @private + */ + this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); +} + +AbstractFilter.prototype.constructor = AbstractFilter; +module.exports = AbstractFilter; + +/** + * Syncs a uniform between the class object and the shaders. + * + */ +AbstractFilter.prototype.syncUniform = function (uniform) +{ + for (var i = 0, j = this.shaders.length; i < j; ++i) + { + this.shaders[i].syncUniform(uniform) + } +} + +/* +AbstractFilter.prototype.apply = function (frameBuffer) +{ + // TODO :) +}; +*/ diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/renderers/webgl/utils/ObjectRenderer.js b/src/core/renderers/webgl/utils/ObjectRenderer.js index a07a9b7..cf5b4e7 100644 --- a/src/core/renderers/webgl/utils/ObjectRenderer.js +++ b/src/core/renderers/webgl/utils/ObjectRenderer.js @@ -1,3 +1,6 @@ + +var WebGLManager = require('../managers/WebGLManager'); + /** * * @class @@ -7,14 +10,11 @@ */ function ObjectRenderer(renderer) { - /** - * The renderer used by this object renderer. - * - * @member {WebGLRenderer} - */ - this.renderer = renderer; + WebGLManager.call(this, renderer); } + +ObjectRenderer.prototype = Object.create(WebGLManager.prototype); ObjectRenderer.prototype.constructor = ObjectRenderer; module.exports = ObjectRenderer; @@ -36,9 +36,4 @@ ObjectRenderer.prototype.render = function (/* object */) { // render the object -}; - -ObjectRenderer.prototype.destroy = function () -{ - this.renderer = null; -}; +}; \ No newline at end of file diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 5b3929f..71a3c20 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -110,61 +110,37 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl; + var gl = this.gl, + pm = this.projectionMatrix; + gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); + var frame = this.frame; - if(this.frame) - { - // gl.viewport(this.frame.x, this.frame.y, this.frame.width, this.frame.height); - - gl.viewport(0,0, this.frame.width, this.frame.height); - - if (!this.root) - { - - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = 1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = -1 - this.frame.y * this.projectionMatrix.d; - } - else - { - this.projectionMatrix.a = 1/(this.frame.width)*2; - this.projectionMatrix.d = -1/(this.frame.height)*2; - - this.projectionMatrix.tx = -1 - this.frame.x * this.projectionMatrix.a; - this.projectionMatrix.ty = 1 - this.frame.y * this.projectionMatrix.d; - } - - - } - else + if(frame) { if (!this.root) { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = 1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = 1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = -1 - frame.y * pm.d; } else { - this.projectionMatrix.a = 1/(this.width)*2; - this.projectionMatrix.d = -1/(this.height)*2; + pm.a = 1 / frame.width*2; + pm.d = -1 / frame.height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; + pm.tx = -1 - frame.x * pm.a; + pm.ty = 1 - frame.y * pm.d; } - - // this.projectionMatrix.tx = -1; - // this.projectionMatrix.ty = 1; + gl.viewport(0,0, frame.width, frame.height); - gl.viewport(0, 0, this.width, this.height); } + + // gl.viewport(frame.x, frame.y, frame.width, frame.height); }; /** diff --git a/src/core/renderers/webgl/utils/SpriteMaskFilter.js b/src/core/renderers/webgl/utils/SpriteMaskFilter.js new file mode 100644 index 0000000..1b5cc37 --- /dev/null +++ b/src/core/renderers/webgl/utils/SpriteMaskFilter.js @@ -0,0 +1,146 @@ +var AbstractFilter = require('./AbstractFilter'); + +/** + * The AlphaMaskFilter class uses the pixel values from the specified texture (called the displacement map) to perform a displacement of an object. + * You can use this filter to apply all manor of crazy warping effects + * Currently the r property of the texture is used to offset the x and the g property of the texture is used to offset the y. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment + */ +function AlphaMaskFilter(sprite) +{ + AbstractFilter.call(this); + + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; + + // set the uniforms + this.uniforms = { + mask: { type: 'sampler2D', value: texture }, + mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, + dimensions: { type: '4fv', value: [0, 0, 0, 0] }, + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } + }; + + if (texture.baseTexture.hasLoaded) + { + this.uniforms.mask.value.x = texture.width; + this.uniforms.mask.value.y = texture.height; + } + else + { + this.boundLoadedFunction = this.onTextureLoaded.bind(this); + + texture.baseTexture.on('loaded', this.boundLoadedFunction); + } + + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + + this.fragmentSrc = [ + 'precision mediump float;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'uniform sampler2D uSampler;', + 'uniform sampler2D mask;', + 'uniform vec2 mapDimensions;', + 'uniform vec4 dimensions;', + 'uniform vec2 offset;', + + 'void main()', + '{', + ' vec4 original = texture2D(uSampler, vTextureCoord);', + ' vec4 masky = texture2D(mask, vMaskCoord);', + ' original *= (masky.r * masky.a);', + ' gl_FragColor = original;', + '}' + ].join('\n'); +} + +AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); +AlphaMaskFilter.prototype.constructor = AlphaMaskFilter; +module.exports = AlphaMaskFilter; + +/** + * Sets the map dimensions uniforms when the texture becomes available. + * + */ +AlphaMaskFilter.prototype.onTextureLoaded = function () +{ + this.uniforms.mapDimensions.value.x = this.uniforms.mask.value.width; + this.uniforms.mapDimensions.value.y = this.uniforms.mask.value.height; + + this.uniforms.mask.value.baseTexture.off('loaded', this.boundLoadedFunction); +}; + +Object.defineProperties(AlphaMaskFilter.prototype, { + /** + * The texture used for the displacement map. Must be power of 2 sized texture. + * + * @member {Texture} + * @memberof AlphaMaskFilter# + */ + map: { + get: function () + { + return this.uniforms.mask.value; + }, + set: function (value) + { + this.uniforms.mask.value = value; + } + }, + + /** + * The offset used to move the displacement map. + * + * @member {Point} + * @memberof AlphaMaskFilter# + */ + offset: { + get: function() + { + return this.uniforms.offset.value; + }, + set: function(value) + { + this.uniforms.offset.value = value; + } + } +}); diff --git a/src/core/sprites/Sprite.js b/src/core/sprites/Sprite.js index 2dc47ff..249e448 100644 --- a/src/core/sprites/Sprite.js +++ b/src/core/sprites/Sprite.js @@ -23,6 +23,7 @@ { DisplayObjectContainer.call(this); + /** * The anchor sets the origin point of the texture. * The default is 0,0 this means the texture's origin is the top left diff --git a/src/core/sprites/webgl/SpriteBatchRenderer.js b/src/core/sprites/webgl/SpriteBatchRenderer.js index ceb41f5..8c246c8 100644 --- a/src/core/sprites/webgl/SpriteBatchRenderer.js +++ b/src/core/sprites/webgl/SpriteBatchRenderer.js @@ -137,10 +137,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteBatchRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -154,7 +150,7 @@ * * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteBatchRenderer.prototype.setupContext = function () +SpriteBatchRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; diff --git a/src/core/sprites/webgl/SpriteRenderer.js b/src/core/sprites/webgl/SpriteRenderer.js index 4f4eab4..ccfdf8f 100644 --- a/src/core/sprites/webgl/SpriteRenderer.js +++ b/src/core/sprites/webgl/SpriteRenderer.js @@ -1,7 +1,6 @@ var ObjectRenderer = require('../../renderers/webgl/utils/ObjectRenderer'), Shader = require('../../renderers/webgl/shaders/Shader'), - WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'), - SpriteShader = require('./SpriteShader'); + WebGLRenderer = require('../../renderers/webgl/WebGLRenderer'); /** * @author Mat Groves @@ -152,10 +151,6 @@ */ this.shader = null; - this.setupContext(); - - // handle when the renderer's context changes. - this.renderer.on('context', this.setupContext.bind(this)); } SpriteRenderer.prototype = Object.create(ObjectRenderer.prototype); @@ -170,12 +165,12 @@ * @private * @param gl {WebGLContext} the current WebGL drawing context */ -SpriteRenderer.prototype.setupContext = function () +SpriteRenderer.prototype.onContextChange = function () { var gl = this.renderer.gl; // setup default shader - this.shader = new SpriteShader(this.renderer.shaderManager); + this.shader = this.renderer.shaderManager.defaultShader; // create a couple of buffers this.vertexBuffer = gl.createBuffer(); @@ -402,13 +397,11 @@ // set shader function??? this.renderer.shaderManager.setShader(shader); - if (shader.dirty) - { - shader.syncUniforms(); - } + ///console.log(shader.uniforms.projectionMatrix); // both thease only need to be set if they are changing.. // set the projection + gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); } } diff --git a/src/core/sprites/webgl/SpriteShader.js b/src/core/sprites/webgl/SpriteShader.js deleted file mode 100644 index 563ecdf..0000000 --- a/src/core/sprites/webgl/SpriteShader.js +++ /dev/null @@ -1,42 +0,0 @@ -var Shader = require('../../renderers/webgl/shaders/Shader'); - -/** - * @class - * @namespace PIXI - * @param shaderManager {WebGLShaderManager} The webgl shader manager this shader works for. - */ -function SpriteShader(shaderManager) -{ - Shader.call(this, - shaderManager, - null, - // fragment shader - [ - 'precision lowp float;', - - 'varying vec2 vTextureCoord;', - 'varying vec4 vColor;', - - 'uniform sampler2D uSampler;', - - 'void main(void){', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * vColor ;', - '}' - ].join('\n'), - // custom uniforms - { - tint: { type: '3f', value: [0, 0, 0] }, - flipY: { type: '1f', value: 0 }, - alpha: { type: '1f', value: 0 }, - translationMatrix: { type: 'mat3', value: new Float32Array(9) } - }, - { - aTextureCoord: 0, - aColor: 0 - } - ); -} - -SpriteShader.prototype = Object.create(Shader.prototype); -SpriteShader.prototype.constructor = SpriteShader; -module.exports = SpriteShader; diff --git a/src/core/textures/Texture.js b/src/core/textures/Texture.js index e97f0aa..0b4dfa9 100644 --- a/src/core/textures/Texture.js +++ b/src/core/textures/Texture.js @@ -186,10 +186,17 @@ var baseTexture = this.baseTexture; baseTexture.removeEventListener('loaded', this.onLoaded); + // TODO this code looks confusing.. boo to abusing getters and setterss! if (this.noFrame) { this.frame = new math.Rectangle(0, 0, baseTexture.width, baseTexture.height); } + else + { + this.frame = this._frame; + } + + this.dispatchEvent( { type: 'update', content: this } ); }; diff --git a/src/core/utils/pluginTarget.js b/src/core/utils/pluginTarget.js index 3f0e9ae..011dbc3 100644 --- a/src/core/utils/pluginTarget.js +++ b/src/core/utils/pluginTarget.js @@ -9,23 +9,30 @@ * * pluginTarget.mixin(MyObject); */ -function pluginTarget(obj) { +function pluginTarget(obj) +{ obj.__plugins = {}; - obj.registerPlugin = function (pluginName, ctor) { + obj.registerPlugin = function (pluginName, ctor) + { obj.__plugins[pluginName] = ctor; }; - obj.prototype.initPlugins = function () { + obj.prototype.initPlugins = function () + { this.plugins = {}; - for (var o in obj.__plugins) { + for (var o in obj.__plugins) + { this.plugins[o] = new (obj.__plugins[o])(this); } }; - obj.prototype.destroyPlugins = function () { - for (var o in this.plugins) { + obj.prototype.destroyPlugins = function () + { + + for (var o in this.plugins) + { this.plugins[o].destroy(); this.plugins[o] = null; } diff --git a/src/extras/StripShader.js b/src/extras/StripShader.js index 3e7c47f..c7b69b3 100644 --- a/src/extras/StripShader.js +++ b/src/extras/StripShader.js @@ -53,4 +53,4 @@ StripShader.prototype.constructor = StripShader; module.exports = StripShader; -core.WebGLShaderManager.registerPlugin('stripShader', StripShader); +//core.WebGLShaderManager.registerPlugin('stripShader', StripShader); diff --git a/src/filters/AbstractFilter.js b/src/filters/AbstractFilter.js deleted file mode 100644 index a214f82..0000000 --- a/src/filters/AbstractFilter.js +++ /dev/null @@ -1,65 +0,0 @@ -/** - * This is the base class for creating a PIXI filter. Currently only WebGL supports filters. - * If you want to make a custom filter this should be your base class. - * - * @class - * @namespace PIXI - * @param fragmentSrc {string|string[]} The fragment source in an array of strings. - * @param uniforms {object} An object containing the uniforms for this filter. - */ -function AbstractFilter(fragmentSrc, uniforms) -{ - /** - * An array of passes - some filters contain a few steps this array simply stores the steps in a liniear fashion. - * For example the blur filter has two passes blurX and blurY. - * - * @member {AbstractFilter[]} - * @private - */ - this.passes = [this]; - - /** - * @member {Shader[]} - * @private - */ - this.shaders = []; - - /** - * @member {number} - */ - this.padding = 0; - - /** - * @member {object} - * @private - */ - this.uniforms = uniforms || {}; - - /** - * @member {string[]} - * @private - */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); -} - -AbstractFilter.prototype.constructor = AbstractFilter; -module.exports = AbstractFilter; - -/** - * Syncs the uniforms between the class object and the shaders. - * - */ -AbstractFilter.prototype.syncUniforms = function () -{ - for (var i = 0, j = this.shaders.length; i < j; ++i) - { - this.shaders[i].dirty = true; - } -}; - -/* -AbstractFilter.prototype.apply = function (frameBuffer) -{ - // TODO :) -}; -*/ diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js index 97f60c3..0426b94 100644 --- a/src/filters/AlphaMaskFilter.js +++ b/src/filters/AlphaMaskFilter.js @@ -10,18 +10,24 @@ * @namespace PIXI * @param texture {Texture} The texture used for the displacement map * must be power of 2 texture at the moment */ -function AlphaMaskFilter(texture) +function AlphaMaskFilter(sprite) { AbstractFilter.call(this); - texture.baseTexture._powerOf2 = true; + this.sprite = sprite; + + var texture = sprite.texture; + // texture.baseTexture._powerOf2 = true; // set the uniforms this.uniforms = { mask: { type: 'sampler2D', value: texture }, mapDimensions: { type: '2f', value: { x: 1, y: 5112 } }, dimensions: { type: '4fv', value: [0, 0, 0, 0] }, - offset: { type: '2f', value: { x: 0, y: 0 } } + offset: { type: '2f', value: { x: 0, y: 0 } }, + otherMatrix: { type: 'mat3', value: new Float32Array(1, 0, 0, + 0, 1, 0, + 0, 0, 1) } }; if (texture.baseTexture.hasLoaded) @@ -36,9 +42,38 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } + this.vertexSrc = [ + + + 'attribute vec2 aVertexPosition;', + 'attribute vec2 aTextureCoord;', + 'attribute vec4 aColor;', + + 'uniform mat3 projectionMatrix;', + 'uniform mat3 otherMatrix;', + + 'varying vec2 vMaskCoord;', + 'varying vec2 vTextureCoord;', + 'varying vec4 vColor;', + + 'const vec2 center = vec2(-1.0, 1.0);', + + 'void main(void){', + + 'gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);', + ' vTextureCoord = aTextureCoord;', + + ' vMaskCoord = ( otherMatrix * vec3( aTextureCoord, 1.0) ).xy;', + + ' vColor = vec4(aColor.rgb * aColor.a, aColor.a);', + + '}' + ].join('\n'); + this.fragmentSrc = [ 'precision mediump float;', + 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', 'varying vec4 vColor;', @@ -50,20 +85,23 @@ 'void main()', '{', - ' vec2 mapCords = vTextureCoord.xy;', + /* ' vec2 mapCords = vTextureCoord.xy;', ' mapCords += (dimensions.zw + offset)/ dimensions.xy ;', ' mapCords.y *= -1.0;', ' mapCords.y += 1.0;', ' mapCords *= dimensions.xy / mapDimensions;', + ' float maskAlpha = texture2D(mask, mapCords).r;',*/ ' vec4 original = texture2D(uSampler, vTextureCoord);', - ' float maskAlpha = texture2D(mask, mapCords).r;', - ' original *= maskAlpha;', + + ' vec4 masky = texture2D(mask, vMaskCoord);', + + ' original *= (masky.r * masky.a);', //' original.rgb *= maskAlpha;', ' gl_FragColor = original;', - //' gl_FragColor = gl_FragColor;', + //' gl_FragColor = masky;', '}' - ]; + ].join('\n'); } AlphaMaskFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/ColorMatrixFilter.js b/src/filters/ColorMatrixFilter.js index 6750c3b..462dea1 100644 --- a/src/filters/ColorMatrixFilter.js +++ b/src/filters/ColorMatrixFilter.js @@ -33,7 +33,8 @@ 'void main(void)', '{', - ' gl_FragColor = texture2D(uSampler, vTextureCoord) * 2.0;', + ' gl_FragColor = texture2D(uSampler, vTextureCoord) * 1.0;', + // ' gl_FragColor *= gl_FragCoord.x;', // ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);',//texture2D(uSampler, vTextureCoord) * matrix;', '}' ].join('\n'); diff --git a/src/filters/index.js b/src/filters/index.js index eba8fcb..c65a115 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -9,7 +9,6 @@ * @namespace PIXI */ module.exports = { - AbstractFilter: require('./AbstractFilter'), AlphaMaskFilter: require('./AlphaMaskFilter'), AsciiFilter: require('./AsciiFilter'), BlurFilter: require('./BlurFilter'), diff --git a/src/index.js b/src/index.js index f864027..98ffd7c 100644 --- a/src/index.js +++ b/src/index.js @@ -2,8 +2,8 @@ // plugins: core.extras = require('./extras'); -core.filters = require('./filters'); +//core.filters = require('./filters'); core.interaction = require('./interaction'); -core.loaders = require('./loaders'); -core.spine = require('./spine'); +//core.loaders = require('./loaders'); +//core.spine = require('./spine'); core.text = require('./text');