diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/filters/BlurFilter.js b/src/filters/BlurFilter.js index 1dea1e9..e50f425 100644 --- a/src/filters/BlurFilter.js +++ b/src/filters/BlurFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), BlurXFilter = require('./BlurXFilter'), BlurYFilter = require('./BlurYFilter'); @@ -17,13 +17,26 @@ this.blurXFilter = new BlurXFilter(); this.blurYFilter = new BlurYFilter(); - this.passes = [this.blurXFilter, this.blurYFilter]; + this.defaultFilter = new AbstractFilter(); } -BlurFilter.prototype = Object.create(AbstractFilter.prototype); +BlurFilter.prototype = Object.create( AbstractFilter.prototype ); BlurFilter.prototype.constructor = BlurFilter; module.exports = BlurFilter; +BlurFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + Object.defineProperties(BlurFilter.prototype, { /** * Sets the strength of both the blurX and blurY properties simultaneously diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/filters/BlurFilter.js b/src/filters/BlurFilter.js index 1dea1e9..e50f425 100644 --- a/src/filters/BlurFilter.js +++ b/src/filters/BlurFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), BlurXFilter = require('./BlurXFilter'), BlurYFilter = require('./BlurYFilter'); @@ -17,13 +17,26 @@ this.blurXFilter = new BlurXFilter(); this.blurYFilter = new BlurYFilter(); - this.passes = [this.blurXFilter, this.blurYFilter]; + this.defaultFilter = new AbstractFilter(); } -BlurFilter.prototype = Object.create(AbstractFilter.prototype); +BlurFilter.prototype = Object.create( AbstractFilter.prototype ); BlurFilter.prototype.constructor = BlurFilter; module.exports = BlurFilter; +BlurFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + Object.defineProperties(BlurFilter.prototype, { /** * Sets the strength of both the blurX and blurY properties simultaneously diff --git a/src/filters/BlurXFilter.js b/src/filters/BlurXFilter.js index a8f5dca..cf02f09 100644 --- a/src/filters/BlurXFilter.js +++ b/src/filters/BlurXFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurXFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + // fragment shader + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,10 +37,15 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); + } -BlurXFilter.prototype = Object.create(AbstractFilter.prototype); +BlurXFilter.prototype = Object.create( AbstractFilter.prototype ); BlurXFilter.prototype.constructor = BlurXFilter; module.exports = BlurXFilter; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/filters/BlurFilter.js b/src/filters/BlurFilter.js index 1dea1e9..e50f425 100644 --- a/src/filters/BlurFilter.js +++ b/src/filters/BlurFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), BlurXFilter = require('./BlurXFilter'), BlurYFilter = require('./BlurYFilter'); @@ -17,13 +17,26 @@ this.blurXFilter = new BlurXFilter(); this.blurYFilter = new BlurYFilter(); - this.passes = [this.blurXFilter, this.blurYFilter]; + this.defaultFilter = new AbstractFilter(); } -BlurFilter.prototype = Object.create(AbstractFilter.prototype); +BlurFilter.prototype = Object.create( AbstractFilter.prototype ); BlurFilter.prototype.constructor = BlurFilter; module.exports = BlurFilter; +BlurFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + Object.defineProperties(BlurFilter.prototype, { /** * Sets the strength of both the blurX and blurY properties simultaneously diff --git a/src/filters/BlurXFilter.js b/src/filters/BlurXFilter.js index a8f5dca..cf02f09 100644 --- a/src/filters/BlurXFilter.js +++ b/src/filters/BlurXFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurXFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + // fragment shader + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,10 +37,15 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); + } -BlurXFilter.prototype = Object.create(AbstractFilter.prototype); +BlurXFilter.prototype = Object.create( AbstractFilter.prototype ); BlurXFilter.prototype.constructor = BlurXFilter; module.exports = BlurXFilter; diff --git a/src/filters/BlurYFilter.js b/src/filters/BlurYFilter.js index b9d0638..c5fa4a5 100644 --- a/src/filters/BlurYFilter.js +++ b/src/filters/BlurYFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurYFilter() { - AbstractFilter.call(this); + AbstractFilter.call(this, - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,7 +37,12 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); } BlurYFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/filters/BlurFilter.js b/src/filters/BlurFilter.js index 1dea1e9..e50f425 100644 --- a/src/filters/BlurFilter.js +++ b/src/filters/BlurFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), BlurXFilter = require('./BlurXFilter'), BlurYFilter = require('./BlurYFilter'); @@ -17,13 +17,26 @@ this.blurXFilter = new BlurXFilter(); this.blurYFilter = new BlurYFilter(); - this.passes = [this.blurXFilter, this.blurYFilter]; + this.defaultFilter = new AbstractFilter(); } -BlurFilter.prototype = Object.create(AbstractFilter.prototype); +BlurFilter.prototype = Object.create( AbstractFilter.prototype ); BlurFilter.prototype.constructor = BlurFilter; module.exports = BlurFilter; +BlurFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + Object.defineProperties(BlurFilter.prototype, { /** * Sets the strength of both the blurX and blurY properties simultaneously diff --git a/src/filters/BlurXFilter.js b/src/filters/BlurXFilter.js index a8f5dca..cf02f09 100644 --- a/src/filters/BlurXFilter.js +++ b/src/filters/BlurXFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurXFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + // fragment shader + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,10 +37,15 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); + } -BlurXFilter.prototype = Object.create(AbstractFilter.prototype); +BlurXFilter.prototype = Object.create( AbstractFilter.prototype ); BlurXFilter.prototype.constructor = BlurXFilter; module.exports = BlurXFilter; diff --git a/src/filters/BlurYFilter.js b/src/filters/BlurYFilter.js index b9d0638..c5fa4a5 100644 --- a/src/filters/BlurYFilter.js +++ b/src/filters/BlurYFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurYFilter() { - AbstractFilter.call(this); + AbstractFilter.call(this, - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,7 +37,12 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); } BlurYFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/GrayFilter.js b/src/filters/GrayFilter.js index 2b94906..380d165 100644 --- a/src/filters/GrayFilter.js +++ b/src/filters/GrayFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'); +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'); /** * This greyscales the palette of your Display Objects. @@ -9,14 +9,8 @@ */ function GrayFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - gray: { type: '1f', value: 1 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -31,7 +25,12 @@ ' gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126*gl_FragColor.r + 0.7152*gl_FragColor.g + 0.0722*gl_FragColor.b), gray);', // ' gl_FragColor = gl_FragColor;', '}' - ]; + ].join('\n'), + // set the uniforms + { + gray: { type: '1f', value: 1 } + }); + } GrayFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/filters/BlurFilter.js b/src/filters/BlurFilter.js index 1dea1e9..e50f425 100644 --- a/src/filters/BlurFilter.js +++ b/src/filters/BlurFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), BlurXFilter = require('./BlurXFilter'), BlurYFilter = require('./BlurYFilter'); @@ -17,13 +17,26 @@ this.blurXFilter = new BlurXFilter(); this.blurYFilter = new BlurYFilter(); - this.passes = [this.blurXFilter, this.blurYFilter]; + this.defaultFilter = new AbstractFilter(); } -BlurFilter.prototype = Object.create(AbstractFilter.prototype); +BlurFilter.prototype = Object.create( AbstractFilter.prototype ); BlurFilter.prototype.constructor = BlurFilter; module.exports = BlurFilter; +BlurFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + Object.defineProperties(BlurFilter.prototype, { /** * Sets the strength of both the blurX and blurY properties simultaneously diff --git a/src/filters/BlurXFilter.js b/src/filters/BlurXFilter.js index a8f5dca..cf02f09 100644 --- a/src/filters/BlurXFilter.js +++ b/src/filters/BlurXFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurXFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + // fragment shader + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,10 +37,15 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); + } -BlurXFilter.prototype = Object.create(AbstractFilter.prototype); +BlurXFilter.prototype = Object.create( AbstractFilter.prototype ); BlurXFilter.prototype.constructor = BlurXFilter; module.exports = BlurXFilter; diff --git a/src/filters/BlurYFilter.js b/src/filters/BlurYFilter.js index b9d0638..c5fa4a5 100644 --- a/src/filters/BlurYFilter.js +++ b/src/filters/BlurYFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurYFilter() { - AbstractFilter.call(this); + AbstractFilter.call(this, - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,7 +37,12 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); } BlurYFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/GrayFilter.js b/src/filters/GrayFilter.js index 2b94906..380d165 100644 --- a/src/filters/GrayFilter.js +++ b/src/filters/GrayFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'); +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'); /** * This greyscales the palette of your Display Objects. @@ -9,14 +9,8 @@ */ function GrayFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - gray: { type: '1f', value: 1 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -31,7 +25,12 @@ ' gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126*gl_FragColor.r + 0.7152*gl_FragColor.g + 0.0722*gl_FragColor.b), gray);', // ' gl_FragColor = gl_FragColor;', '}' - ]; + ].join('\n'), + // set the uniforms + { + gray: { type: '1f', value: 1 } + }); + } GrayFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/index.js b/src/filters/index.js index c65a115..e4c1726 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -9,27 +9,28 @@ * @namespace PIXI */ module.exports = { - AlphaMaskFilter: require('./AlphaMaskFilter'), - AsciiFilter: require('./AsciiFilter'), + BloomFilter: require('./BloomFilter'), + // AlphaMaskFilter: require('./AlphaMaskFilter'), + // AsciiFilter: require('./AsciiFilter'), BlurFilter: require('./BlurFilter'), BlurXFilter: require('./BlurXFilter'), BlurYFilter: require('./BlurYFilter'), - ColorMatrixFilter: require('./ColorMatrixFilter'), - ColorStepFilter: require('./ColorStepFilter'), - ConvolutionFilter: require('./ConvolutionFilter'), - CrossHatchFilter: require('./CrossHatchFilter'), - DisplacementFilter: require('./DisplacementFilter'), - DotScreenFilter: require('./DotScreenFilter'), - GrayFilter: require('./GrayFilter'), - InvertFilter: require('./InvertFilter'), - NoiseFilter: require('./NoiseFilter'), - NormalMapFilter: require('./NormalMapFilter'), - PixelateFilter: require('./PixelateFilter'), - RGBSplitFilter: require('./RGBSplitFilter'), - SepiaFilter: require('./SepiaFilter'), - SmartBlurFilter: require('./SmartBlurFilter'), - TiltShiftFilter: require('./TiltShiftFilter'), - TiltShiftXFilter: require('./TiltShiftXFilter'), - TiltShiftYFilter: require('./TiltShiftYFilter'), - TwistFilter: require('./TwistFilter') + // ColorMatrixFilter: require('./ColorMatrixFilter'), + // ColorStepFilter: require('./ColorStepFilter'), + // ConvolutionFilter: require('./ConvolutionFilter'), + // CrossHatchFilter: require('./CrossHatchFilter'), + // DisplacementFilter: require('./DisplacementFilter'), + // DotScreenFilter: require('./DotScreenFilter'), + GrayFilter: require('./GrayFilter') + // InvertFilter: require('./InvertFilter'), + // NoiseFilter: require('./NoiseFilter'), + // NormalMapFilter: require('./NormalMapFilter'), + // PixelateFilter: require('./PixelateFilter'), + // RGBSplitFilter: require('./RGBSplitFilter'), + // SepiaFilter: require('./SepiaFilter'), + // SmartBlurFilter: require('./SmartBlurFilter'), + // TiltShiftFilter: require('./TiltShiftFilter'), + // TiltShiftXFilter: require('./TiltShiftXFilter'), + // TiltShiftYFilter: require('./TiltShiftYFilter'), + // TwistFilter: require('./TwistFilter') }; diff --git a/src/core/display/DisplayObjectContainer.js b/src/core/display/DisplayObjectContainer.js index 146e5ba..ad76d00 100644 --- a/src/core/display/DisplayObjectContainer.js +++ b/src/core/display/DisplayObjectContainer.js @@ -616,7 +616,7 @@ * @private */ DisplayObjectContainer.prototype._generateCachedSprite = function () -{ +{/* var bounds = this.getLocalBounds(); if (!this._cachedSprite) @@ -646,7 +646,7 @@ this._cachedSprite.anchor.x = -(bounds.x / bounds.width); this._cachedSprite.anchor.y = -(bounds.y / bounds.height); - this._filters = tempFilters; + this._filters = tempFilters;*/ }; /** diff --git a/src/core/graphics/index.js b/src/core/graphics/index.js new file mode 100644 index 0000000..4fc777d --- /dev/null +++ b/src/core/graphics/index.js @@ -0,0 +1,105 @@ +/** + * @file Main export of the PIXI core library + * @author Mat Groves + * @copyright 2013-2015 GoodBoyDigital + * @license {@link https://github.com/GoodBoyDigital/pixi.js/blob/master/LICENSE|MIT License} + */ + +/** + * @namespace PIXI + */ +var core = module.exports = { + CONST: require('./const'), + + // utils + utils: require('./utils'), + math: require('./math'), + + // display + DisplayObject: require('./display/DisplayObject'), + DisplayObjectContainer: require('./display/DisplayObjectContainer'), + + Stage: require('./display/DisplayObjectContainer'), + + Sprite: require('./sprites/Sprite'), + SpriteBatch: require('./sprites/SpriteBatch'), + SpriteRenderer: require('./sprites/webgl/SpriteRenderer'), + SpriteBatchRenderer: require('./sprites/webgl/SpriteBatchRenderer'), + + // primitives + Graphics: require('./graphics/Graphics'), + GraphicsData: require('./graphics/GraphicsData'), + GraphicsRenderer: require('./graphics/webgl/GraphicsRenderer'), + + // textures + Texture: require('./textures/Texture'), + BaseTexture: require('./textures/BaseTexture'), + RenderTexture: require('./textures/RenderTexture'), + VideoBaseTexture: require('./textures/VideoBaseTexture'), + + // renderers - canvas + CanvasRenderer: require('./renderers/canvas/CanvasRenderer'), + CanvasGraphics: require('./renderers/canvas/utils/CanvasGraphics'), + CanvasBuffer: require('./renderers/canvas/utils/CanvasBuffer'), + + // renderers - webgl + WebGLRenderer: require('./renderers/webgl/WebGLRenderer'), + WebGLShaderManager: require('./renderers/webgl/managers/WebGLShaderManager'), + Shader: require('./renderers/webgl/shaders/Shader'), + + /** + * This helper function will automatically detect which renderer you should be using. + * WebGL is the preferred renderer as it is a lot faster. If webGL is not supported by + * the browser then this function will return a canvas renderer + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * @param [noWebGL=false] {Boolean} prevents selection of WebGL renderer, even if such is present + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRenderer: function (width, height, options, noWebGL) + { + width = width || 800; + height = height || 600; + + if (!noWebGL && require('webgl-enabled')()) + { + return new core.WebGLRenderer(width, height, options); + } + + return new core.CanvasRenderer(width, height, options); + }, + + /** + * This helper function will automatically detect which renderer you should be using. This function is very + * similar to the autoDetectRenderer function except that is will return a canvas renderer for android. + * Even thought both android chrome supports webGL the canvas implementation perform better at the time of writing. + * This function will likely change and update as webGL performance improves on these devices. + * + * @param width=800 {number} the width of the renderers view + * @param height=600 {number} the height of the renderers view + * @param [options] {object} The optional renderer parameters + * @param [options.view] {HTMLCanvasElement} the canvas to use as a view, optional + * @param [options.transparent=false] {boolean} If the render view is transparent, default false + * @param [options.antialias=false] {boolean} sets antialias (only applicable in chrome at the moment) + * @param [options.preserveDrawingBuffer=false] {boolean} enables drawing buffer preservation, enable this if you + * need to call toDataUrl on the webgl context + * @param [options.resolution=1] {number} the resolution of the renderer retina would be 2 + * + * @return {WebGLRenderer|CanvasRenderer} Returns WebGL renderer if available, otherwise CanvasRenderer + */ + autoDetectRecommendedRenderer: function (width, height, options) + { + var isAndroid = /Android/i.test(navigator.userAgent); + + return core.autoDetectRenderer(width, height, options, isAndroid); + } +}; diff --git a/src/core/graphics/webgl/GraphicsRenderer.js b/src/core/graphics/webgl/GraphicsRenderer.js index 8c6c25c..3c72bf5 100644 --- a/src/core/graphics/webgl/GraphicsRenderer.js +++ b/src/core/graphics/webgl/GraphicsRenderer.js @@ -385,6 +385,7 @@ var vecPos = verts.length/6; + //TODO use this https://github.com/mapbox/earcut var triangles = utils.PolyK.Triangulate(recPoints); // diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js index 5a2fac1..78df77f 100644 --- a/src/core/renderers/webgl/WebGLRenderer.js +++ b/src/core/renderers/webgl/WebGLRenderer.js @@ -347,9 +347,9 @@ { this.blendModeManager.setBlendMode(CONST.blendModes.NORMAL); - this.currentRenderTarget = renderTarget; + this.setRenderTarget(renderTarget); - this.currentRenderTarget.activate(); + // reset the render session data.. this.drawCount = 0; @@ -377,6 +377,12 @@ this.currentRenderer.start(); }; +WebGLRenderer.prototype.setRenderTarget = function (renderTarget) +{ + this.currentRenderTarget = renderTarget; + this.currentRenderTarget.activate(); +}; + /** * Resizes the webGL view to the specified width and height. * diff --git a/src/core/renderers/webgl/managers/FilterManager.js b/src/core/renderers/webgl/managers/FilterManager.js index 9acd869..29e97a3 100644 --- a/src/core/renderers/webgl/managers/FilterManager.js +++ b/src/core/renderers/webgl/managers/FilterManager.js @@ -1,8 +1,5 @@ var WebGLManager = require('./WebGLManager'), - FilterTexture = require('../utils/FilterTexture'), - RenderTarget = require('../utils/RenderTarget'); - DefaultShader = require('../shaders/TextureShader'), - + RenderTarget = require('../utils/RenderTarget'), Quad = require('../utils/Quad'), math = require('../../../math'); @@ -37,6 +34,8 @@ //TODO make this dynamic! this.textureSize = new math.Rectangle(0, 0, 800, 600); + this.currentFrame = null; + this.tempMatrix = new math.Matrix(); } @@ -51,18 +50,18 @@ var gl = this.renderer.gl; this.quad = new Quad(gl); -} +}; /** * @param renderer {WebGLRenderer} * @param buffer {ArrayBuffer} */ -FilterManager.prototype.begin = function (buffer) +FilterManager.prototype.begin = function () { //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); + this.filterStack[0].bounds = this.renderer.currentRenderTarget.size; }; /** @@ -72,30 +71,25 @@ */ 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.capFilterArea( bounds ); - var texture = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + var texture = this.getRenderTarget(); - // texture. - // TODO setting frame is lame.. - texture.frame = bounds;//new math.Rectangle(, 0, this.realSize.width, this.realSize.height); - texture.activate(); + // set the frame so the render target knows how much to render! + texture.frame = bounds; + this.renderer.setRenderTarget( texture ); + // clear the texture.. texture.clear(); - this.renderer.currentRenderTarget = texture; - // TODO get rid of object creation! this.filterStack.push({ renderTarget:texture, - filter:filters, - bounds:bounds + filter:filters }); }; @@ -108,59 +102,78 @@ FilterManager.prototype.popFilter = function () { var filterData = this.filterStack.pop(); - + var previousFilterData = this.filterStack[this.filterStack.length-1]; var input = filterData.renderTarget; + var output = previousFilterData.renderTarget; + + // use program var gl = this.renderer.gl; var filter = filterData.filter[0]; - var shader = filter.getShader(renderer) + + this.currentFrame = input.frame; - // ;filter.shaders[gl.id]; + this.quad.map(this.textureSize, input.frame); + // TODO.. this probably only needs to be done once! + gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); - // shader.syncUniforms(); + // this.__TEMP__ = filter.sprite; + filter.applyFilter( this.renderer, input, output ); - this.quad.map(this.textureSize, filterData.bounds); + this.returnRenderTarget( input ); + return filterData.filter; +}; +FilterManager.prototype.getRenderTarget = function () +{ + var renderTarget = this.texturePool.pop() || new RenderTarget(this.renderer.gl, this.textureSize.width, this.textureSize.height); + renderTarget.frame = this.currentFrame; + return renderTarget; +}; +FilterManager.prototype.returnRenderTarget = function (renderTarget) +{ + this.texturePool.push( renderTarget ); +}; + +FilterManager.prototype.applyFilter = function (shader, inputTarget, outputTarget) +{ + var gl = this.renderer.gl; + + this.renderer.setRenderTarget( outputTarget ); // set the shader this.renderer.shaderManager.setShader(shader); + + shader.uniforms.projectionMatrix.value = this.renderer.currentRenderTarget.projectionMatrix.toArray(true); - // RENDER - - gl.bindBuffer(gl.ARRAY_BUFFER, this.quad.vertexBuffer); - + //TODO can this be optimised? + shader.syncUniforms(); + gl.vertexAttribPointer(shader.attributes.aVertexPosition, 2, gl.FLOAT, false, 0, 0); gl.vertexAttribPointer(shader.attributes.aTextureCoord, 2, gl.FLOAT, false, 0, 2 * 4 * 4); - gl.vertexAttribPointer(shader.attributes.aColor, 1, gl.FLOAT, false, 0, 0); + gl.vertexAttribPointer(shader.attributes.aColor, 4, gl.FLOAT, false, 0, 4 * 4 * 4); - gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.quad.indexBuffer); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, inputTarget.texture); - // draw the filter... + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0 ); - var previousFilter = this.filterStack[this.filterStack.length-1]; - this.renderer.currentRenderTarget = previousFilter.renderTarget; - - this.renderer.currentRenderTarget.frame = previousFilter.bounds; - this.renderer.currentRenderTarget.activate(); - - gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // var m = this.calculateMappedMatrix(inputTarget.frame, this.__TEMP__) - 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)); - +// gl.uniformMatrix3fv(shader.uniforms.projectionMatrix._location, false, this.renderer.currentRenderTarget.projectionMatrix.toArray(true)); + // 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; + var maskTexture = shader.uniforms.mask.value.baseTexture; if (!maskTexture._glTextures[gl.id]) { @@ -169,35 +182,20 @@ else { // bind the texture - gl.bindTexture(gl.TEXTURE_2D, filter.uniforms.mask.value.baseTexture._glTextures[gl.id]); + gl.bindTexture(gl.TEXTURE_2D, shader.uniforms.mask.value.baseTexture._glTextures[gl.id]); } - // set uniform to texture index - gl.uniform1i(filter.uniforms.mask._location, 1); + gl.uniform1i(shader.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; }; -FilterManager.prototype.applyFilter = function (inputTarget, outputTarget, blendMode) -{ - - - // assuming theese things are the same size! - // -} // TODO playing around here.. this is temporary - (will end up in the shader) FilterManager.prototype.calculateMappedMatrix = function (filterArea, sprite) @@ -264,7 +262,7 @@ // transform.scale( translateScaleX , translateScaleY ); // return transform; -} +}; FilterManager.prototype.capFilterArea = function (filterArea) { @@ -289,7 +287,7 @@ { filterArea.height = this.textureSize.height - filterArea.y; } -} +}; /** * Destroys the filter and removes it from the filter stack. @@ -297,8 +295,6 @@ */ FilterManager.prototype.destroy = function () { - var gl = this.renderer.gl; - this.filterStack = null; this.offsetY = 0; diff --git a/src/core/renderers/webgl/utils/AbstractFilter.js b/src/core/renderers/webgl/utils/AbstractFilter.js index e288d6d..a29f5de 100644 --- a/src/core/renderers/webgl/utils/AbstractFilter.js +++ b/src/core/renderers/webgl/utils/AbstractFilter.js @@ -1,3 +1,5 @@ +var DefaultShader = require('../shaders/TextureShader'); + /** * 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. @@ -35,11 +37,15 @@ */ this.uniforms = uniforms || {}; + /** * @member {string[]} * @private */ - this.fragmentSrc = typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + this.fragmentSrc = fragmentSrc; + + //typeof fragmentSrc === 'string' ? fragmentSrc.split('') : (fragmentSrc || []); + } AbstractFilter.prototype.constructor = AbstractFilter; @@ -64,14 +70,16 @@ } return shader; -} +}; -AbstractFilter.prototype.applyWebGL = function (renderer, inputs, output, blendMode) +AbstractFilter.prototype.applyFilter = function (renderer, input, output) { - var gl = renderer.gl; + var filterManager = renderer.filterManager, + shader = this.getShader(renderer); - -} + // draw the filter... + filterManager.applyFilter(shader, input, output); +}; /** * Syncs a uniform between the class object and the shaders. diff --git a/src/core/renderers/webgl/utils/AlphaMaskFilter.js b/src/core/renderers/webgl/utils/AlphaMaskFilter.js index 1b5cc37..1d00cae 100644 --- a/src/core/renderers/webgl/utils/AlphaMaskFilter.js +++ b/src/core/renderers/webgl/utils/AlphaMaskFilter.js @@ -42,9 +42,7 @@ texture.baseTexture.on('loaded', this.boundLoadedFunction); } - this.vertexSrc = [ - - + this.vertexSrc = [ 'attribute vec2 aVertexPosition;', 'attribute vec2 aTextureCoord;', 'attribute vec4 aColor;', @@ -56,22 +54,17 @@ '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);', + '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;', + 'precision lowp float;', 'varying vec2 vMaskCoord;', 'varying vec2 vTextureCoord;', @@ -79,11 +72,8 @@ 'uniform sampler2D uSampler;', 'uniform sampler2D mask;', - 'uniform vec2 mapDimensions;', - 'uniform vec4 dimensions;', - 'uniform vec2 offset;', - 'void main()', + 'void main(void)', '{', ' vec4 original = texture2D(uSampler, vTextureCoord);', ' vec4 masky = texture2D(mask, vMaskCoord);', diff --git a/src/core/renderers/webgl/utils/Quad.js b/src/core/renderers/webgl/utils/Quad.js index 249d17b..39e1313 100644 --- a/src/core/renderers/webgl/utils/Quad.js +++ b/src/core/renderers/webgl/utils/Quad.js @@ -23,13 +23,13 @@ 0,1 ]); - var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); - +// var white = (0xFFFFFF >> 16) + (0xFFFFFF & 0xff00) + ((0xFFFFFF & 0xff) << 16) + (1 * 255 << 24); + //TODO convert this to a 32 unsigned int array this.colors = new Float32Array([ - white, - white, - white, - white + 1,1,1,1, + 1,1,1,1, + 1,1,1,1, + 1,1,1,1 ]); this.indices = new Uint16Array([ @@ -40,7 +40,7 @@ this.indexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); - gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 4) * 4, gl.DYNAMIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER, (8 + 8 + 16) * 4, gl.DYNAMIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, this.indices, gl.STATIC_DRAW); diff --git a/src/core/renderers/webgl/utils/RenderTarget.js b/src/core/renderers/webgl/utils/RenderTarget.js index 71a3c20..5789327 100644 --- a/src/core/renderers/webgl/utils/RenderTarget.js +++ b/src/core/renderers/webgl/utils/RenderTarget.js @@ -15,6 +15,8 @@ */ var RenderTarget = function(gl, width, height, scaleMode, root) { + //TODO Resolution could go here ( eg low res blurs ) + /** * @property gl * @type WebGLContext @@ -35,8 +37,7 @@ */ this.texture = null; - this.width = 0; - this.height = 0; + this.size = new math.Rectangle(0, 0, 1, 1); this.resolution = 1; @@ -110,39 +111,43 @@ RenderTarget.prototype.activate = function() { //TOOD refactor usage of frame.. - var gl = this.gl, - pm = this.projectionMatrix; + var gl = this.gl; gl.bindFramebuffer(gl.FRAMEBUFFER, this.frameBuffer); - var frame = this.frame; + var projectionFrame = this.frame || this.size; - if(frame) - { - if (!this.root) - { - pm.a = 1 / frame.width*2; - pm.d = 1 / frame.height*2; + // TODO add a dirty flag to this of a setter for the frame? + this.calculateProjection( projectionFrame ); - pm.tx = -1 - frame.x * pm.a; - pm.ty = -1 - frame.y * pm.d; - } - else - { - pm.a = 1 / frame.width*2; - pm.d = -1 / frame.height*2; - - pm.tx = -1 - frame.x * pm.a; - pm.ty = 1 - frame.y * pm.d; - } - - gl.viewport(0,0, frame.width, frame.height); - - } - - // gl.viewport(frame.x, frame.y, frame.width, frame.height); + gl.viewport(0,0, projectionFrame.width, projectionFrame.height); }; +RenderTarget.prototype.calculateProjection = function( projectionFrame ) +{ + var pm = this.projectionMatrix; + + pm.identity(); + + if (!this.root) + { + pm.a = 1 / projectionFrame.width*2; + pm.d = 1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = -1 - projectionFrame.y * pm.d; + } + else + { + pm.a = 1 / projectionFrame.width*2; + pm.d = -1 / projectionFrame.height*2; + + pm.tx = -1 - projectionFrame.x * pm.a; + pm.ty = 1 - projectionFrame.y * pm.d; + } +}; + + /** * Resizes the texture to the specified width and height * @@ -155,40 +160,29 @@ width = width | 0; height = height | 0; - if (this.width === width && this.height === height) { + if (this.size.width === width && this.size.height === height) { return; } - this.width = width; - this.height = height; - - this.projectionMatrix = new math.Matrix(); + this.size.width = width; + this.size.height = height; if (!this.root) { var gl = this.gl; - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = 1/height*2; - - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = -1; - gl.bindTexture(gl.TEXTURE_2D, this.texture); gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width , height , 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // update the stencil buffer width and height gl.bindRenderbuffer(gl.RENDERBUFFER, this.stencilBuffer); gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width , height ); } - else - { - this.projectionMatrix.a = 1/width*2; - this.projectionMatrix.d = -1/height*2; - this.projectionMatrix.tx = -1; - this.projectionMatrix.ty = 1; - } + var projectionFrame = this.frame || this.size; + + this.calculateProjection( projectionFrame ); }; /** diff --git a/src/core/textures/RenderTexture.js b/src/core/textures/RenderTexture.js index bc280bb..a19f95e 100644 --- a/src/core/textures/RenderTexture.js +++ b/src/core/textures/RenderTexture.js @@ -299,7 +299,7 @@ // this.renderer.spriteRenderer.dirty = true; -this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); + this.renderer.renderDisplayObject(displayObject, this.textureBuffer);//this.projection, this.textureBuffer.frameBuffer); // this.renderer.spriteRenderer.dirty = true; diff --git a/src/filters/AlphaMaskFilter.js b/src/filters/AlphaMaskFilter.js deleted file mode 100644 index 0426b94..0000000 --- a/src/filters/AlphaMaskFilter.js +++ /dev/null @@ -1,157 +0,0 @@ -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()', - '{', - /* ' 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);', - - ' vec4 masky = texture2D(mask, vMaskCoord);', - - ' original *= (masky.r * masky.a);', - //' original.rgb *= maskAlpha;', - ' gl_FragColor = original;', - //' gl_FragColor = masky;', - '}' - ].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/filters/BloomFilter.js b/src/filters/BloomFilter.js new file mode 100644 index 0000000..32f46e6 --- /dev/null +++ b/src/filters/BloomFilter.js @@ -0,0 +1,99 @@ +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), + BlurXFilter = require('./BlurXFilter'), + BlurYFilter = require('./BlurYFilter'), + CONST = require('../core/const'); + +/** + * The BloomFilter applies a Gaussian blur to an object. + * The strength of the blur can be set for x- and y-axis separately. + * + * @class + * @extends AbstractFilter + * @namespace PIXI + */ +function BloomFilter() +{ + AbstractFilter.call(this); + + this.blurXFilter = new BlurXFilter(); + this.blurYFilter = new BlurYFilter(); + + this.defaultFilter = new AbstractFilter(); +} + +BloomFilter.prototype = Object.create( AbstractFilter.prototype ); +BloomFilter.prototype.constructor = BloomFilter; +module.exports = BloomFilter; + +BloomFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.defaultFilter.applyFilter(renderer, input, output); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + renderer.blendModeManager.setBlendMode( CONST.blendModes.SCREEN ); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + +Object.defineProperties(BloomFilter.prototype, { + /** + * Sets the strength of both the blurX and blurY properties simultaneously + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blur: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = this.blurYFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurX property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurX: { + get: function () + { + return this.blurXFilter.blur; + }, + set: function (value) + { + this.blurXFilter.blur = value; + } + }, + + /** + * Sets the strength of the blurY property + * + * @member {number} + * @memberOf BloomFilter# + * @default 2 + */ + blurY: { + get: function () + { + return this.blurYFilter.blur; + }, + set: function (value) + { + this.blurYFilter.blur = value; + } + } +}); diff --git a/src/filters/BlurFilter.js b/src/filters/BlurFilter.js index 1dea1e9..e50f425 100644 --- a/src/filters/BlurFilter.js +++ b/src/filters/BlurFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), BlurXFilter = require('./BlurXFilter'), BlurYFilter = require('./BlurYFilter'); @@ -17,13 +17,26 @@ this.blurXFilter = new BlurXFilter(); this.blurYFilter = new BlurYFilter(); - this.passes = [this.blurXFilter, this.blurYFilter]; + this.defaultFilter = new AbstractFilter(); } -BlurFilter.prototype = Object.create(AbstractFilter.prototype); +BlurFilter.prototype = Object.create( AbstractFilter.prototype ); BlurFilter.prototype.constructor = BlurFilter; module.exports = BlurFilter; +BlurFilter.prototype.applyFilter = function (renderer, input, output) +{ + var filterManager = renderer.filterManager; + + var renderTarget = filterManager.getRenderTarget(); + + this.blurXFilter.applyFilter(renderer, input, renderTarget); + + this.blurYFilter.applyFilter(renderer, renderTarget, output); + + filterManager.returnRenderTarget( renderTarget ); +}; + Object.defineProperties(BlurFilter.prototype, { /** * Sets the strength of both the blurX and blurY properties simultaneously diff --git a/src/filters/BlurXFilter.js b/src/filters/BlurXFilter.js index a8f5dca..cf02f09 100644 --- a/src/filters/BlurXFilter.js +++ b/src/filters/BlurXFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurXFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + // fragment shader + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,10 +37,15 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); + } -BlurXFilter.prototype = Object.create(AbstractFilter.prototype); +BlurXFilter.prototype = Object.create( AbstractFilter.prototype ); BlurXFilter.prototype.constructor = BlurXFilter; module.exports = BlurXFilter; diff --git a/src/filters/BlurYFilter.js b/src/filters/BlurYFilter.js index b9d0638..c5fa4a5 100644 --- a/src/filters/BlurYFilter.js +++ b/src/filters/BlurYFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'), +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'), blurFactor = 1 / 7000; /** @@ -10,14 +10,9 @@ */ function BlurYFilter() { - AbstractFilter.call(this); + AbstractFilter.call(this, - // set the uniforms - this.uniforms = { - blur: { type: '1f', value: 1 / 512 } - }; - - this.fragmentSrc = [ + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -42,7 +37,12 @@ ' gl_FragColor = sum;', '}' - ]; + ].join('\n'), + + // set the uniforms + { + blur: { type: '1f', value: 1 / 512 } + }); } BlurYFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/GrayFilter.js b/src/filters/GrayFilter.js index 2b94906..380d165 100644 --- a/src/filters/GrayFilter.js +++ b/src/filters/GrayFilter.js @@ -1,4 +1,4 @@ -var AbstractFilter = require('./AbstractFilter'); +var AbstractFilter = require('../core/renderers/webGL/utils/AbstractFilter'); /** * This greyscales the palette of your Display Objects. @@ -9,14 +9,8 @@ */ function GrayFilter() { - AbstractFilter.call(this); - - // set the uniforms - this.uniforms = { - gray: { type: '1f', value: 1 } - }; - - this.fragmentSrc = [ + AbstractFilter.call(this, + [ 'precision mediump float;', 'varying vec2 vTextureCoord;', @@ -31,7 +25,12 @@ ' gl_FragColor.rgb = mix(gl_FragColor.rgb, vec3(0.2126*gl_FragColor.r + 0.7152*gl_FragColor.g + 0.0722*gl_FragColor.b), gray);', // ' gl_FragColor = gl_FragColor;', '}' - ]; + ].join('\n'), + // set the uniforms + { + gray: { type: '1f', value: 1 } + }); + } GrayFilter.prototype = Object.create(AbstractFilter.prototype); diff --git a/src/filters/index.js b/src/filters/index.js index c65a115..e4c1726 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -9,27 +9,28 @@ * @namespace PIXI */ module.exports = { - AlphaMaskFilter: require('./AlphaMaskFilter'), - AsciiFilter: require('./AsciiFilter'), + BloomFilter: require('./BloomFilter'), + // AlphaMaskFilter: require('./AlphaMaskFilter'), + // AsciiFilter: require('./AsciiFilter'), BlurFilter: require('./BlurFilter'), BlurXFilter: require('./BlurXFilter'), BlurYFilter: require('./BlurYFilter'), - ColorMatrixFilter: require('./ColorMatrixFilter'), - ColorStepFilter: require('./ColorStepFilter'), - ConvolutionFilter: require('./ConvolutionFilter'), - CrossHatchFilter: require('./CrossHatchFilter'), - DisplacementFilter: require('./DisplacementFilter'), - DotScreenFilter: require('./DotScreenFilter'), - GrayFilter: require('./GrayFilter'), - InvertFilter: require('./InvertFilter'), - NoiseFilter: require('./NoiseFilter'), - NormalMapFilter: require('./NormalMapFilter'), - PixelateFilter: require('./PixelateFilter'), - RGBSplitFilter: require('./RGBSplitFilter'), - SepiaFilter: require('./SepiaFilter'), - SmartBlurFilter: require('./SmartBlurFilter'), - TiltShiftFilter: require('./TiltShiftFilter'), - TiltShiftXFilter: require('./TiltShiftXFilter'), - TiltShiftYFilter: require('./TiltShiftYFilter'), - TwistFilter: require('./TwistFilter') + // ColorMatrixFilter: require('./ColorMatrixFilter'), + // ColorStepFilter: require('./ColorStepFilter'), + // ConvolutionFilter: require('./ConvolutionFilter'), + // CrossHatchFilter: require('./CrossHatchFilter'), + // DisplacementFilter: require('./DisplacementFilter'), + // DotScreenFilter: require('./DotScreenFilter'), + GrayFilter: require('./GrayFilter') + // InvertFilter: require('./InvertFilter'), + // NoiseFilter: require('./NoiseFilter'), + // NormalMapFilter: require('./NormalMapFilter'), + // PixelateFilter: require('./PixelateFilter'), + // RGBSplitFilter: require('./RGBSplitFilter'), + // SepiaFilter: require('./SepiaFilter'), + // SmartBlurFilter: require('./SmartBlurFilter'), + // TiltShiftFilter: require('./TiltShiftFilter'), + // TiltShiftXFilter: require('./TiltShiftXFilter'), + // TiltShiftYFilter: require('./TiltShiftYFilter'), + // TwistFilter: require('./TwistFilter') }; diff --git a/src/index.js b/src/index.js index 98ffd7c..9910c8d 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,7 @@ // 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');